Ввод/вывод в C++ реализуется либо с помощью функций, унаследованных от библиотеки С, либо с помощью потоков C++. Смешивать эти два способа в одной программе можно только синхронизировав ввод с помощью функции sync_with_stdio(). Каждый способ имеет свои преимущества. Преимущество использования потоков в том, что они легче в использовании в простых случаях ввода/вывода, не требующих форматирования, а, главное, потоковые операции можно переопределить для собственных классов. Ввод/вывод в стиле С удобнее использовать при форматированном выводе в программах, не использующих объектно-ориентированную технику.
Для использования функций ввода/вывода в стиле С необходимо подключить к программе заголовочный файл <stdio.h> или <cstdio>. При вводе/выводе данные рассматриваются как поток байтов. Физически поток представляет собой файл или устройство (например, клавиатуру или дисплей, рассматривающиеся как частный случай файла).
Работа с потоком начинается с его открытия. Поток можно открыть для чтения и/или записи в двоичном или текстовом режиме. Функция открытия потока имеет формат:
При успешном открытии потока функция возвращает указатель на предопределенную структуру типа FILE, содержащую всю необходимую для работы с потоком информацию, или NULL в противном случае. Первый параметр – имя открываемого файла в виде С-строки, второй – режим открытия файла:
"r" – файл открывается для чтения;
"w" – открывается пустой файл для записи (если файл существует, он стирается);
"а" – файл открывается для добавления информации в его конец;
"r+" – файл открывается для чтения и записи (файл должен существовать);
"w+" – открывается пустой файл для чтения и записи (если файл существует, он стирается);
"а+" – файл открывается для чтения и добавления информации в его конец.
Режим открытия может также содержать символы t (текстовый режим) или b (двоичный режим), отличающиеся обработкой символов перехода на новую строку. По умолчанию файл открывается в текстовом режиме, при котором комбинация символов «возврат каретки» и «перевод строки» (0x13 0x10) при вводе преобразуются в одиночный символ перевода строки (при выводе выполняется обратное преобразование). В двоичном режиме эти преобразования не выполняются.
Пример.
FILE * f = fopen("d:\\cpp\\clata", "rb+");
Указатель f используется в дальнейших операциях с потоком. Его передают функциям ввода/вывода в качестве параметра.
При открытии потока с ним связывается область памяти, называемая буфером. При выводе вся информация направляется в буфер и накапливается там до заполнения буфера или до закрытия потока. Чтение осуществляется блоками, равными размеру буфера, и данные читаются из буфера. Буферизация позволяет более быстро и эффективно обмениваться информацией с внешними устройствами. Следует иметь в виду, что при аварийном завершении программы выходной буфер может быть не выгружен, и возможна потеря данных. С помощью функций setbuf и setvbuf можно управлять размерами и наличием буферов.
Существует пять предопределенных потоков, которые открываются в начале работы программы: стандартный ввод stdin, стандартный вывод stdout, стандартный вывод сообщений об ошибках stderr, стандартный дополнительный поток stdaux и стандартная печать stdprn. Первые три потока по умолчанию относятся к консоли. Эти указатели можно использовать в любой функции ввода/вывода там, где требуется указатель потока.
Ввод/вывод в поток можно осуществлять различными способами: в виде последовательности байтов, в виде символов и строк или с использованием форматных преобразований. Для каждого вида операций определен свой набор функций.
Операции ввода/вывода выполняются, начиная с текущей позиции потока, определяемой положением указателя потока. Указатель устанавливается при открытии на начало или конец файла (в соответствии с режимом открытия) и изменяется автоматически после каждой операции, ввода/вывода. Текущее положение указателя можно получить с помощью функций ftell и fgetpos и задать явным образом с помощью функций fseek и fsetpos. Эти функции нельзя использовать для стандартных потоков. Основными функциями ввода/вывода потока являются:
- Чтение и запись потока байтов выполняют функции fread и fwrite.
- Чтение символа из потока – getc, fgetc, из стандартного потока stdin – getchar.
- Запись символа в поток – putс, fputc, в стандартный поток stdout – putchar.
- Чтение строки из потока – fgets, из стандартного потока stdin – gets.
- Запись строки в поток – fputs, в стандартный поток stdout – puts.
- Форматированный ввод из потока – fscant, из стандартного потока stdin – scant, из строки – sscanf.
- Форматированный вывод в поток – fprintf, в стандартный поток stdout – printf, в строку – sprintf.
Поток закрывается либо при завершении программы, либо явным образом с помощью функции fclose:
int fclose(FILE*);
Перед закрытием потока информация из связанных с ним буферов выгружается на диск. Рекомендуется всегда явным образом закрывать потоки, открытые для записи, чтобы избежать потери данных.
Функции работы с потоком возвращают значения, которые рекомендуется анализировать в программе и обрабатывать ошибочные ситуации, возникающие, например, при открытии существующих файлов или чтении из потока. При работе с файлами часто используются функции feof и ferror:
int feof (FILE*) возвращает не равное нулю значение, если достигнут конец файла, в противном случае 0;
int ferror (FILE*) возвращает не равное нулю значение, если обнаружена ошибка ввода/вывода, в противном случае 0.