В главе "Основы ввода-вывода в языке C++" были даны общие представления о потоковых объектах cin, cout и cerr и операторах потокового ввода-вывода << и >>. В настоящей главе мы поговорим о стандартных классах C++, управляющих работой этих объектов.
Иерархия классов ввода-вывода
Все потоковые классы порождены от одного класса, являющегося базовым в иерархии, — ios. Исключение составляют лишь классы буферизованных потоков, базовым для которых служит класс streambuf. Всех их можно разделить на четыре категории, как показано в табл. 15.1.
На рис. 15.1 схематически изображена иерархия классов ввода-вывода, порожденных от класса ios.
Классы семейства ios предоставляют программный интерфейс и обеспечивают необходимое форматирование обрабатываемых данных, тогда как непосредственную обработку данных выполняют классы семейства streambuf, управляющие обменом данными между буфером потока и конечным устройством (рис. 15.2).
Таблица 15.1. Категории классов ввода-вывода | |
Класс | Описание |
ios | Содержит базовые средства управления потоками, является родительским для других классов ввода-вывода (файл IOSTREAM.H) |
Потоковый ввод | |
istream | Содержит общие средства потокового ввода, является родительским для других классов ввода (файл IOSTREAM.H) |
ifstream | Предназначен для ввода данных из файлов (файл FSTREAM.H) |
istream_withassign | Поддерживает операцию присваивания; существует предопределенный объект cin данного класса, по умолчание читающий данные из стандартного входного потока, но благодаря операции присваивания этот объект может быть переадресован на различные объекты класса istream(файл IOSTREAM.H) |
istrstream | Предназначен для ввода данных из строковых буферов (файл STRSTREA.H) |
Потоковый вывод | |
ostream | Содержит общие средства потокового вывода, является родительским для других классов вывода (файл IOSTREAM.H) |
ofstream | Предназначен для вывода данных в файлы (файл FSTREAM.H) |
ostream_withassign | Поддерживает операцию присваивания; существуют предопределенные объекты cout, cerr и clog данного класса, по умолчанию выводящие данные в стандартный выходной поток, но благодаря операции присваивания их можно переадресовать на различные объекты класса ostream (файл IOSTREAM.H) |
ostrstream | Предназначен для вывода данных в строковые буферы (файл STRSTREA.H) |
Потоковый ввод-вывод | |
iostream | Содержит общие средства потокового ввода-вывода, является родительским для других классов ввода-вывода (файл IOSTREAM.H) |
fstream | Предназначен для организации файлового ввода-вывода (файл FSTREAM.H) |
strstream | Предназначен для ввода-вывода строк (файл STBLSTREA.H) |
stdiostream | Поддерживает работу с системными средствами стандартного ввода-вывода, существует для совместимости со старыми функциями ввода-вывода (файл STDIOSTR.H) |
Буферизованные потоки | |
streambuf | Содержит общие средства управления буферами потоков, является родительским для других буферных классов (файл IOSTREAM.H) |
filebuf | Предназначен для управления буферами дисковых файлов (файл FSTREAM.H) |
strstreambuf | Предназначен для управления строковыми буферами, хранящимися в памяти (файл STRSTREA.H) |
stdiobuf | Осуществляет буферизацию дискового ввода-вывода с помощью стандартных системных функций (файл STDIOSTR.H) |
Рис. 15.1. Иерархия классов ввода-вывода порожденных от
ios
Рис. 15.2. Иерархия классов, порожденных от streambuf
Файловый ввод
Основные функции управления потоковым вводом сосредоточены в классе istream. С каждым из объектов этого класса и его производных связан объект класса streambuf. Эти классы работают в связке: первый осуществляет форматирование, а второй управляет низкоуровневым буферизованным вводом. Функции класса istream, доступные его потомкам, перечислены в табл. 15.2.
Класс ifstream является потомком класса istream, ориентированным на чтение данных из файлов. Его конструктор автоматически создает объект класса filebuf, управляющий низкоуровневой работой с файлом, включая поддержку буфера чтения. Функции класса ifstream перечислены в табл. 15.3.
Таблица 15.3. Функции класса ifstream |
|
ФУНКЦИЯ | Описание |
open | Открывает файл для чтения, связывая с ним объект класса filebuf |
close | Закрывает файл |
setbuf | Передает указанный символьный буфер в распоряжение объекта класса filebuf |
setmode | Задает режим доступа к файлу: двоичный (константа filebuf:: binary) или текстовый (константа filebuf:: text) |
attach | Связывает указанный открытый файл с объектом класса filebuf |
rdbuf | Возвращает указатель на объект класса filebuf |
fd | Возвращает дескриптор файла |
is_open | Проверяет, открыт ли файл, связанный с потоком |
В следующей программе из файла читаются данные блоками по 80 символов, которые затем выводятся на экран:
//
// ifstream.cpp
//В этой программе на языке C++ демонстрируется
// использование класса ifstreamдля чтения данных из файла.
//
#include <fstream.h>
#define iCOLUMNS 80
void main(void)
{
charcOneLine[iCOLUMNS];
fstream ifMyInputStream("IFSTREAM.CPP");
while(ifMylnputStream) {
ifMylnputStream.getline(cOneLine, iCOLUMNS);
cout << '\n' << cOneLine;
}
ifMylnputStream.close();
Конструктор клаccа ifstream создает объект ifMylnputStream, связывая с ним файл IFSTREAM.CPP, который открывается для чтения (по умолчанию в текстовом режиме). Этот объект можно использовать в условных операторах, проверяя его на равенство нулю, что означает достижение конца файла.
Функция getline(), унаследованная от класса istream, читает в массив cOneLineстроку текста длиной iCOLUMNS(80 символов). Ввод будет прекращен при обнаружении символа новой строки \n, конца файла или, если ни одно из этих событий не произошло, 79-го по счету символа (последним записывается символ \0).
После окончания вывода всего файла он закрывается командой close().
Файловый вывод
Основные функции управления потоковым выводом сосредоточены в классе ostream. С каждым из объектов этого класса и его производных связан объект класса streambuf. Эти классы работают в связке: первый осуществляет форматирование, а второй управляет низкоуровневым буферизованным выводом. Функции класса ostream, доступные его потомкам, перечислены в табл. 15.4.
Таблица 15.4. Функции класса ostream | |
Функция | Описание |
opfx | Вызывается перед каждой операцией записи для проверки наличия ошибок в потоке |
оsfx | Вызывается после каждой операции записи для очистки буфера |
put | Записывает в поток одиночный байт |
write | Записывает в поток требуемое число байтов |
flush | Очищает буфер потока; аналогичное действие выполняет манипулятор flush |
seekp | Перемещает маркер, обозначающий текущую позицию записи, на требуемую позицию в потоке |
tellp | Возвращает позицию маркера записи |
Класс ofstream является потомком класса ostream, ориентированным на запись данных в файлы. Его конструктор автоматически создает объект класса filebuf, управляющий низкоуровневой работой с файлом, включая поддержку буфера записи. Класс ofstream содержит такой же набор функций, что и класс ifstream(см. табл. 15.3).
В следующей программе в файл дважды записывается одна и та же строка — посимвольно и вся целиком.
//
// ofstream. cpp
// В этой программе на языке C++ демонстрируется
// использование класса ofstreamдля записи данных в
// файл, открытый в текстовом режиме.
#include <fstream.h>
#include <string.h>
#define iSTRING_MAX 40
void main (void) {
int i = 0;
char pszString [iSTRING_MAX] •= "Записываемая строка\n";
// файл по умолчанию открывается
в текстовом режиме
ofstream ofMyOutputStream("OFSTREAM.OUT") ;
// строка выводится символ за символом;
// обратите внимание, что символ '\n' .
// преобразуется в два символа
while (pszString[i] != '\0'){
ofMyOutputStream.put (pszStringfi]
) ;
cout<< !'\nПозиция маркера записи: " << ofMyOutputStream.tellp()
;
i++; }
// запись всей строки целиком
ofMyOutputStream.write(pszString,
strlen(pszString));
cout<< "\nНовая позиция маркера записи: " << ofMyOutputStream.tellp();
ofMyOutputStream.close() ; -}
Вот результаты работы программы:
Позиция маркера записи:
1
Позиция маркера записи:
2
Позиция маркера записи:
3
.
.
.
Позиция маркера записи: 18
Позиция маркера записи: 19
Позиция маркера записи: 21
Новая позиция маркера записи: 42
Цикл while последовательно, символ за символом, с помощью функции put() записывает содержимое строки pszstring в выходной поток. После вывода каждого символа Вызывается функция tellp(),возвращающая текущую позицию маркера записи. Результатам работы этой функции следует уделить немного внимания.
Строка pszString содержит 20 символов плюс концевой нулевой символ (\0), итого — 21. На хотя, если судить по выводимой информации, в поток записывается 21 символ, последним из них будет не \0. Его вообще не окажется в файле. Дело в том, что при выводе данных в файл, открытый в текстовом режиме, автоматически выполняется преобразование \n = CR/LF, т.е. символ новой строки преобразуется в пару символов возврата каретки и перевода строки. (При чтении происходит обратное преобразование.) Именно поэтому наблюдается "скачок" счетчика: после 19 идет 21. Функция put(), записывающая в файл символ \n, на самом деле помещает в файл два других символа.
Функция write() выполняет такое же преобразование, поэтому конечное значение счетчика равно 42, а не 41.
Двоичные файлы
Следующая программа аналогична предыдущему примеру и иллюстрирует, что произойдет, если ту же самую строку вывести в тот же самый файл, только открытый в двоичном режиме.
//
// binary.срр
// Эта программа является модификацией
предыдущего примера
// и демонстрирует работу с файлом,открытым
в двоичном режиме.
//
#include <fstream. h>
#include <string.h>
#define iSTRING_MAX 40
void main (void) <<
int i = 0;
char pszString [iSTRING_MAX] = "Записываемая строка\n";
// файл открывается в двоичном режиме
ofstream ofMyOutputStream("OFSTREAM.OUT", ios :: binary)
// строка выводится символ за символом;
// обратите внимание, что символ ' \n '
// никак не преобразуется
while (pszString [i]!= '\0') {
ofMyOutputStream.put (pszString [i]);
cout <<' "\nПозиция маркера записи: << ofMyOutputStream.tellpO ;
i++> 1
// запись всей строки целиком .
ofMyOutputStream.write(pszString, strlen(pszString)),
cout<< "\nНовая позиция маркера записи: "
<< ofMyOutputStream.tellp()
;
ofMyOutputStream.close() ;
}
Вот результаты работы программы:
Позиция маркера записи: 1
Позиция маркера записи: 2
Позиция маркера записи: 3
Позиция маркера записи: 18
Позиция маркера записи: 19
Позиция маркера записи: 20
Новая позиция маркера записи: 40
При выводе строки pszString не происходит замены концевого символа \n парой символов возврата каретки и перевода строки, поэтому в поток записывается 20 символов — ровно столько, сколько содержится в строке.
Буферы потоков
В основе всех буферизованных потоков ввода-вывода в C++ лежит класс streambuf. В этом классе описаны основные операции, выполняемые над буферами ввода-вывода (табл. 15.5). Любой класс, порожденный от ios, наследует указатель на объект класса streambuf. Именно последний выполняет реальную работу по вводу и выводу данных.
Объекты класса streambuf управляют фиксированной областью памяти, называемой также областью резервирования. Она, в свою очередь, может быть разделена на область ввода и область вывода, которые могут перекрывать друг друга.
Класс streambuf является абстрактным, т.е. создать его объект напрямую нельзя (его конструктор является защищенным и не может быть вызван из программы). В то же время, имеются три производных от него класса, предназначенные для работы с потоками конкретного типа: filebuf (буферы дисковых файлов), strstreambuf (строковые буферы, хранящиеся в памяти) и stdiobuf (буферизация дискового ввода-вывода с помощью стандартных системных функций). Кроме того, можно создавать собственные классы, порожденные от streambuf, переопределяя его виртуальные функции (табл. 15.6) и настраивая, таким образом, его работу в соответствии с потребностями конкретного приложения. Только из производных классов можно вызывать и многочисленные защищенные функции этого класса, управляющие областью резервирования (табл. 15.7).
Таблица 15.5. Открытые функции класса streambuf | |
Открытая функция | Описание |
in_avail | Возвращает число символов в области ввода |
sgetc | Возвращает символ, на который ссылается указатель области ввода; при этом указатель не перемещается |
snextc | Перемещает указатель области ввода на одну позицию вперед, после чего возвращает текущий символ |
sbumpc | Возвращает текущий символ и затем перемещает указатель области ввода на одну позицию вперед |
stossc | Перемещает указатель области ввода на одну позицию вперед, но не возвращает символ |
sputbaqkc | Перемещает указатель области ввода на одну позицию назад, возвращая символ в буфер |
sgetn | Читает требуемое количество символов из буфера |
out_waiting | Возвращает число символов в области вывода |
sputc | Записывает символ в буфер и перемещает указатель области вывода на одну позицию вперед |
sputn | Записывает требуемое количество символов в буфер и перемещает указатель области вывода на соответствующее число позиций |
dbp | В текстовом виде записывает в стандартный выходной поток различного рода информацию о состоянии буфера |
Таблица 15.6. Виртуальные функции класса streambuf | |
Виртуальная функция | Описание |
sync | Очищает области ввода и вывода |
setbuf | Добавляет к буферу указанную зарезервированную область памяти |
seekoff | Перемещает указатель области ввода или вывода на указанное количество байтов относительно текущей позиции |
seekpos | Перемещает указатель области ввода или вывода на указанную позицию относительно начала потока |
overflow | Очищает область вывода |
underflow | Если область ввода пуста, заполняет ее данными из источника |
pbackfail | Вызывается функцией sputbackc() в случае неудачной попытки вернуться назад на один символ |
Классы файловых потоков, такие как ifstream, ofstream и fstream, содержат встроенный объект класса filebuf, который вызывает низкоуровневые системные функции для управления буферизованным файловым вводом-выводом. Для объектов класса filebuf области ввода и вывода, а также указатели текущей позиции чтения и записи всегда равны друг другу. Функции этого класса перечислены в табл. 15.8.
Таблица 15.8.Функции класса filebuf | |
Функция | Описание |
open | Открывает файл, связывая с ним объект класса filebuf |
close | Закрывает файл, "выталкивая" все содержимое области вывода |
setmode | Задает режим доступа к файлу: двоичный (константа filebuf:: binary) или текстовый (константа filebuf: :text) |
attach | Связывает указанный открытый файл с объектом класса filebuf |
fd | Возвращает дескриптор файла |
is_open | Проверяет, открыт ли файл, связанный с потоком |
В следующей программе создаются два файловых объекта: fbMylnputBuf и fbMyOutputBuf. Оба они открываются в текстовом режиме с помощью функции open(): первый — для чтения, второй — для записи. Если при открытии файлов не возникнет никаких ошибок, то каждый из них связывается с соответствующим объектом класса istream и ostream. Далее в цикле while символы читаются из файла fbMylnputBuf с помощью функции get() класса istream и записываются в файл fbMyOutputBuf с помощью функции put() класса ostream. Подсчет числа строк осуществляется с помощью выражения
iLineCount += (ch == '\n');
Когда в поток помещается символ \n, выражение в скобках возвращает 1 и счетчик iLineCount увеличивается на единицу, в противном случае он остается неизменным.
Оба файла закрываются с помощью функции close().
//
// filebuf.cpp
// Эта программа на языке C++ демонстрирует, как работать
// с объектами класса filebuf.
//
#include <f stream. h>
t#include <process.h>
// содержит прототип функции exit()
void main (void)
{
char ch;
int iLineCount = 0;
filebuf fbMylnputBuf, fbMyOutputBuf;
fbMylnputBuf .open ("FILEBUF.CPP", ios::in); if (fbMylnputBuf .is_open()== 0) (
cerr<< "Невозможно открыть файл для чтения"
exit (1);
}
istream is(SfbMylnputBuf) ;
fbMyOutputBuf.open("output.dat",ios::out); if(fbMyOutputBuf.is_open() == 0) {
cerr<< "Невозможно открыть файл для записи";
exit(2); }
ostream os(SfbMyOutputBuf);
while (is) {
is.get(ch);
os.put(ch);
iLineCount += (ch== '\n');
}
bMylnputBuf. close ();
fbMyOutputBuf.close();
cout << "Файл содержит " << iLineCount << " строк";
}
Класс strstreambuf управляет символьным буфером, расположенным в динамической памяти.
Несколько примеров форматного вывода данных
В первом примере на экран выводится список факториалов чисел от 1 до 25 с выравниванием влево:
//
// fact.cpp
// Эта программа на языке C++ выводит список факториалов чисел от 1
до 25.
//
#include <iostream.h>
main ()
{
double number = 1.0,factorial = 1.0;
cout .precision (0); //0 цифрпослезапятой
cout .setf (ios:: left) ; // выравнивание влево
cout .setf (ios:: fixed) ; // фиксированный формат (безэкспоненты)
for(int i << 0; i < 25;i++) {
factorial *= number++;
cout << factorial << endl; }
return(0);
}
Результаты работы программы будут такими:
1
2
6
24
120
720
5040
40320
362880
3628800
39916800
479001600
6227020800
87178291200
1307674368000
20922789888000
355687428096000
6402373705728000
121645100408832000
2432902008176640000
51090942171709440000
1124000727777607680000
25852016738884976640000
620448401733239439360000
15511210043330985984000000
Во втором примере на экран выводится таблица квадратов и квадратных корней целых чисел от 1 до 15.
//
// sqrt.cpp
// Эта программа на языке C++ строит таблицу квадратов и квадратных
// корней чисел от 1 до 15.
//
#include <iostream.h>
# include <math.h>
main () {
double number =1.0,square, sqroot;
cout << "Число\tKвaдрат\t\tKвадратный корень\n";
cout << " ______________________________________ \n";
cout .setf (ios:: fixed) ; // фиксированный формат (без экспоненты)
for(int i = 1; i < 16;i++) {
square = number * number; // вычисление
квадрата числа
sqroot = sqrt(number); // нахождение корня числа
cout.fill('0');
// заполнение недостающих позиций нулями
cout.width(2); // ширина столбца — минимум 2 символа
cout.precision(0); // 0 цифрпосле запятой
cout << number << "\t";
cout.fill(' '); // использовать пробел в качестве заполнителя cout << square << "\t\t";
cout.precision(6); // 6 цифр
после запятой
cout<< sqroot<< endl;
number++; }
return(0); }
Программа выведет следующую таблицу чисел:
число корень квадратный корень
01 |
1 |
1.000000 |
|
02 |
4 |
1.414214 |
|
03 |
9 |
1.732051 |
|
04 |
16 |
2.000000 |
|
05 |
25 |
2..236068 |
|
06 |
36 |
2.449490 |
|
07 |
49 |
2.645751 |
|
08 |
64 |
2.828427 |
|
09 |
81 |
3.000000 |
|
10 |
100 |
3.162278 |
|
11 |
121 |
3.316625 |
|
12 |
144 |
3.464102 |
|
13 |
169 |
3.605551 |
|
14 |
196 |
3.741657 |
|
15 |
225 |
3.872983 |