В стандартной библиотеке ввода/вывода стандартного Си (заголовочный файл библиотеки - <stdio.h>) имеются внешние переменные-указатели на дескрипторы файлов - стандартных устройств ввода-вывода.
extern FILE *stdin, *stdout, *stderr, *stdaux, *stdprn;
(стандартный ввод, стандартный вывод, регистрация ошибок, дополнительное устройство, устройство печати).
Эти файлы открываются библиотекой автоматически перед выполнением функции main и по умолчанию назначаются на терминал (stdin - клавиатура, stdout, stderr - экран), последовательный порт (stdaux) и принтер (stdprn). stdin и stdout могут быть переназначены в командой строке запуска программы на любые другие файлы.
Потоковые классы представляют объектно-ориентированный вариант функций ANSI-C. Поток данных между источником и приемником при этом обладает следующими свойствами.
Пример шаблона потокового класса.
template<class charT, class traits = ios_traits<charT> > class basic_istream : virtual public basic_ios<charT, traits>;
Библиотека потоковых классов С++ построена на основе двух базовых классов: ios и streambuf .
Класс streambuf обеспечивает организацию и взаимосвязь буферов ввода-вывода, размещаемых в памяти, с физическими устройствами ввода-вывода. Методы и данные класса streambuf программист явно обычно не использует. Этот класс нужен другим классам библиотеки ввода-вывода. Он доступен и программисту для создания новых классов на основе уже существующих.

Класс ios содержит средства для форматированного ввода-вывода и проверки ошибок.

Стандартные потоки (istream, ostream, iostream) служат для работы с терминалом. Строковые потоки (istrstream, ostrstream, strstream) служат для ввода-вывода из строковых буферов, размещенных в памяти. Файловые потоки (ifstream, ofstream, fstream ) служат для работы с файлами.
ios базовый потоковый класс
streambuf буферизация потоков
istream потоки ввода
ostream потоки вывода
iostream двунаправленные потоки
iostream_withassign поток с переопределенной операцией присваивания
istrstream строковые потоки ввода
ostrstream строковые потоки вывода
strstream двунаправленные строковые потоки
ifstream файловые потоки ввода
ofstream файловые потоки вывода
fstream двунаправленные файловые потоки
Следующие объекты-потоки заранее определены и открыты в программе перед вызовом функции main:
extern istream cin; // Стандартный поток ввода с клавиатуры
extern ostream cout; // Стандартный поток вывода на экран
extern ostream cerr; // Стандартный поток вывода сообщений об ошибках (экран)
extern ostream clog; // Стандартный буферизованный поток вывода
// сообщений об ошибках (экран)
Потоковые классы, их методы и данные становятся доступными в программе, если в неё включен нужный заголовочный файл.
iostream.h - для ios, ostream, istream .
strstream.h - для strstream, istrstream, ostrstream .
fstream.h - для fstream, ifstream, ofstream.
Для ввода с потока используются объекты класса istream, для вывода в поток - объекты класса ostream .
В классе istream определены следующие функции:
В классе ostream определены следующие функции:
Для прямого доступа используются следующие функции установки позиции чтения - записи.
При чтении
При записи
Помимо этих функций в классе istream перегружена операция >>, а в классе ostream - <<. Операции << и >> имеют два операнда. Левым операндом является объект класса istream (ostream ), а правым - данное, тип которого задан в языке.
Для того чтобы использовать операции << и >> для всех стандартных типов данных используется соответствующее число перегруженных функций operator << и operator >>. При выполнении операций ввода-вывода в зависимости от типа правого операнда вызывается та или иная перегруженная функция operator .
Поддерживаются следующие типы данных: целые, вещественные, строки (char *). Для вывода - void * (все указатели, отличные от char *, автоматически переводятся к void *). Перегрузка операции >> и << не изменяет их приоритета.
Функции operator << и operator >> возвращают ссылку на тот потоковый объект, который указан слева от знака операции. Таким образом, можно формировать ''цепочки'' операций.
cout << a << b << c; cin >> i >> j >> k;
При вводе-выводе можно выполнять форматирование данных.
Чтобы использовать операции >> и << с данными пользовательских типов, определяемых пользователем, необходимо расширить действие этих операций, введя новые операции-функции. Первым параметром операции-функции должна быть ссылка на объект потокового типа, вторым - ссылка или объект пользовательского типа.
// объявление класса Vector
class Vector
{
public:
Vector(unsigned);
~Vector();
protected:
unsigned v; // размер вектора
float *pv; // указатель на 1-й элемент
friend ostream &operator <<(ostream &, const Vector &);
friend istream &operator >>(istream &, Vector &);
};
// определение некоторых компонентных функций
ostream &operator <<(ostream &strm, const Vector &V)
{
strm << endl;
for (unsigned i = 0; i < V.v; i++)
{
strm.width(5);
strm << V.pv[i] << endl;
}
return strm;
}
istream &operator <<(istream &strm, Vector &V)
{
for (unsigned i = 0; i < V.v; i++)
{
strm >> V.pv[i];
}
return strm;
}
Непосредственное применение операций ввода << и вывода >> к стандартным потокам cout, cin, cerr, clog для данных базовых типов приводит к использованию ''умалчиваемых'' форматов внешнего представления пересылаемых значений.
Форматы представления выводимой информации и правила восприятия данных при вводе могут быть изменены программистом с помощью флагов форматирования. Эти флаги унаследованы всеми потоками из базового класса ios. Флаги форматирования реализованы в виде отдельных фиксированных битов и хранятся в protected компоненте класса long x_flags. Для доступа к ним имеются соответствующие public функции.
Кроме флагов форматирования используются следующие protected компонентные данные класса ios :
int x_width - минимальная ширина поля вывода.
int x_precision - точность представления вещественных чисел (количество цифр дробной части) при выводе;
int x_fill - символ-заполнитель при выводе, пробел - по умолчанию.
Для получения (установки) значений этих полей используются следующие компонентные функции:
int width(void); int width(int); int precision(void); int precision(int); char fill(void); char fill(char);
Несмотря на гибкость и большие возможности управления форматами с помощью компонентных функций класса ios , их применение достаточно громоздко. Более простой способ изменения параметров и флагов форматирования обеспечивают манипуляторы.
Манипуляторами называются специальные функции, позволяющие модифицировать работу потока. Особенность манипуляторов состоит в том, что их можно использовать в качестве правого операнда операции >> или << . В качестве левого операнда, как обычно, используется поток (ссылка на поток), и именно на этот поток воздействует манипулятор. Манипуляторы указывают, например, ширину поля, точность при вычислении с плавающей точкой и т.п.
Для обеспечения работы с манипуляторами в классах istream и ostream имеются следующие перегруженные функции operator.
istream &operator >>(istream &(*_f)(istream &)); ostream &operator <<(ostream &(*_f)(ostream &));
При использовании манипуляторов следует включить заголовочный файл <iomanip.h>, в котором определены встроенные манипуляторы. Манипуляторы действуют на ввод-вывод в поток до внесения новых изменений.
| dec | Устанавливает 10-тичную систему счисления. Воздействует на int и long. |
| Потоки используют основание 10 по умолчанию. | |
| hex | Устанавливает 16-ричную систему счисления. |
| oct | Устанавливает 8-ричную систему счисления. |
| ws | Выбирает из потока ввода символы пропуска. Поток будет читаться |
| до появления символа, отличного от пропуска, или до возникновения | |
| ошибки потока. | |
| endl | Вставляет в поток вывода символ новой строки и затем сбрасывает поток. |
| ends | Вставляет '\0' в поток вывода. |
| flush | Сбрасывает поток вывода. |
setbase()
устанавливает основание счисления к любому из четырех значений:
| 0 | Основание по умолчанию. При выводе 10-тичное, при вводе - числа, |
| начинающиеся с '0', считаются 8-ричными, начинающиеся с '0x', | |
| - 16-ричными. Во всех остальных случаях основание считается 10-тичным. | |
| 8 | Для ввода-вывода используется основание 8. |
| 10 | Для ввода-вывода используется основание 10. |
| 16 | Для ввода-вывода используется основание 16. |
Другие значения игнорируются. Библиотека iostream не поддерживает произвольных оснований, подобных 3, 12 и т.д. Если нужно представить значения по основанию, отличному от 8, 10 или 16, то соответствующее преобразование нужно выполнить явно.
resetiosflags(long) очищает один или более флагов форматирования в ios::x_flags
setiosflags(long) устанавливает один или более флагов форматирования в ios::x_flags
setfill(int) устанавливает символ - заполнитель.
Символ-заполнитель используется для заполнения поля тогда, когда ширина поля больше ширины выведенного значения. Заполнение не будет происходить, если пользователь не указал минимальной ширины поля с помощью манипулятора setw(int) или функции ios::width(int). По умолчанию символом-заполнителем является пробел. Заполнение будет происходить справа, слева, или как-то еще, в зависимости от значения битов ios::adjustfield, установленных обращением к ios::setf(long)
setprecision() устанавливает число цифр после 10-тичной точки в числах с плавающей точкой. Этот манипулятор действует только на потоке вывода setw(int width) Устанавливает ширину следующей вставляемой в поток вывода переменной. Если значение следующей переменной требует для записи меньше места, чем указано, то будет осуществляться заполнение символом-заполнителем, установленным манипулятором setfill(int). Ширина автоматически сбрасывается в 0 после каждой вставки в поток.
Определение пользовательских манипуляторов.
Порядок создания пользовательского манипулятора с параметрами, например для вывода, следующий:
typedef ostream &(*PTF) (ostream &, int, int, char);
class MyManip
{
public:
// конструктор
MyManip(PTF F, int W, int N, char FILL)
: f(F),
w(W),
n(N),
fill(FILL)
{}
protected:
int w;
int n;
char fill;
PTF f;
friend ostream &operator <<(ostream &, MyManip);
};
ostream &operator <<(ostream &out, MyManip my)
{
return my.f(out, my.w, my.n, my.fill);
}
ostream &fmanip(ostream &s, int w, int n, char fill)
{
s.width(w);
s.flags(ios::fixed);
s.precision(n);
s.fill(fill);
return s;
}
MyManip wp(int w, int n, char fill)
{
return MyManip(fmanip, w, n, fill);
}
Для создания пользовательских манипуляторов с параметрами можно использовать макросы, которые содержатся в файле <iomanip.h>:
OMANIP(int) IMANIP(int) IOMANIP(int)
Каждый поток имеет связанное с ним состояние. Состояния потока описываются в классе ios в виде перечисления enum.
public:
enum io_state {
goodbit, // нет ошибки 0Х00
eofbit, // конец файла 0Х01
failbit, // последняя операция не выполнилась 0Х02
badbit, // попытка использования недопустимой операции 0Х04
hardfail // фатальная ошибка 0Х08
};
Флаги, определяющие результат последней операции объектом ios, содержатся в переменной state. Получить значение этой переменной можно с помощью функции int rdstate().
Кроме того, проверить состояние потока можно следующими функциями:
int bad(void); // 1, badbit или hardfail int eof(void); // 1, если eofbit int fail(void); // 1, если failbit, badbit или hardfail int good(void); // 1, если goodbit
Если операция >> используется для новых типов данных, то при её перегрузке необходимо предусмотреть соответствующие проверки.
Потоки для работы с файлами создаются как объекты следующих классов:
ofstream - запись в файл;
ifstream - чтение из файла;
fstream - чтение/запись.
Для создания потоков имеются следующие конструкторы:
Флаги режима определены в классе ios и имеют следующие значения:
in - для чтения
out - для записи
ate - индекс потока помещен в конец файла. Чтение больше не допустимо, выводные данные записываются в конец файла;
app - поток открыт для добавления данных в конец. Независимо от seekp() данные будут записываться в конец;
trunc - усечение существующего потока до нуля;
nocreate - команда открытия потока будет завершена неудачно, если файл не существует;
noreplace - команда открытия потока будет завершена неудачно, если файл существует;
binary - поток открывается для двоичного обмена.
Если при создании потока он не присоединен к файлу, то присоединить существующий поток к файлу можно функцией
void open(const char *name, int mode, int p = filebuf::openprot);
Функция
void fstreambase::close();
сбрасывает буфер потока, отсоединяет поток от файла и закрывает файл. Эту функцию необходимо явно вызвать при изменении режима работы с потоком. Автоматически она вызывается только при завершении программы.
Таким образом, создать поток и связать его с файлом можно тремя способами:
filebuf fbuf;Объект filebuf связывается с устройством (файлом)
fbuf.open("имя", ios::in);
Создается поток и связывается с
filebuf
istream stream(&fbuf);
fstream stream;Открывается файл, который связывается через filebuf с потоком
stream.open("имя", ios::in);
fstream stream("имя", ios::in);