В стандартной библиотеке ввода/вывода стандартного Си (заголовочный файл библиотеки - <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);