Введение. 2
Общие положения. 3
Потоковый ввод-вывод 9
Форматный ввод-вывод. 13
Форматный ввод из входного потока. 15
Литература 17
В стандарте языка Си отсутствуют средства ввода-вывода. Все операции ввода-вывода реализуются с помощью функций, находящихся в библиотеке языка Си, поставляемой в составе конкретной системы программирования Си. Во время работы с файлами данные могут передаваться или в своем внутреннем двоичном представлении или в текстовом формате, то есть в более удобочитаемом виде.
Особенностью языка Си, который впервые был применен три разработке операционной системы UNIX, является отсутствие заранее спланированных структур файлов. Все файлы рассматриваются как неструктурированная последовательность байтов. При таком подходе к организации файлов удалось распространить понятие файла и на различные устройства. В UNIX конкретному устройству соответствует так называемый "специальный файл", а одни и те же функции библиотеки языка Си используются как для обмена данными с файлами, так и для обмена с устройствами.
Библиотека языка Си поддерживает три уровня ввода-вывода: потоковый ввод-вывод, ввод-вывод нижнего уровня и ввод-вывод для консоли и портов. Последний уровень, обеспечивающий удобный специализированный обмен данными с дисплеем и портами ввода-вывода, мы рассматривать не будем в силу его системной зависимости. Например, он различен для MS-DOS, Windows и UNIX.
Язык Си является фундаментом С++. При этом С++ поддерживает всю файловую систему Си. Поэтому при использовании С-кода в С++ нет необходимости менять процедуры ввода-вывода. Хотя при написании программ на С++ обычно более удобно использовать именно систему С++. Это касается, в частности, и использования "iostream.h" взамен "stdio.h", реализующим ввод-вывод. Изучим файловый ввод-вывод в языке Си. Тем более, что это само по себе очень интересно и очень важно для понимания "потоков" и "файлов" как в Си, так и в С++.
В системе ввода-вывода в Си для программ поддерживается единый интерфейс, не зависящий от того, к какому конкретному устройству осуществляется доступ. То есть в Си между программой и устройством находится нечто более общее, чем само устройство. Такое обобщенное устройство ввода или вывода (устройство более высокого уровня абстракции) называется потоком. В то же время конкретное устройство называется файлом. Наша задача - понять, каким обрзом происходит взаимодействие потоков и файлов.
Файловая система Си предназначена для работы с разными устройствами, в том числе с терминалами, дисководами и накопителями. Даже, если какое-то устройство очень сильно отличается от других устройств, буферизованная файловая система все равно представит его в виде логического устройства, которое называется потоком. Все потоки ведут себя похожим образом. И так как они в основном не зависят от физических устройств, то та же функция, которая выполняет запись в дисковый файл, может ту же операцию выполнить и на другом устройстве. Например, на консоли. Потоки бывают двух видов: текстовые и двоичные.
В языке Си файлом может быть все, что угодно, начиная в дискового файла и заканчивая терминалом или принтером. Поток связывают с определенным файлом, выполняя обязательную операцию открытия. Как только файл открыт, можно проводить обмен информацией между ним и программой.
Но не у всех файлов одинаковые возможности. Например, к дисковому файлу прямой доступ возможен, в то время как к некоторым принтерам - он не возможен. Таким образом, вы видите, что напрашивается определенный вывод, являющийся принципом системы ввода-вывода языка Си: все потоки одинаковы, а файлы - нет!
Если файл может поддерживать запросы на местоположение (указатель текущей позиции), то при открытии такого файла указатель текущей позиции в файле устанавливается в начало файла. При чтении каждого символа из файла (или записи в файл) указатель текущей позиции увеличивается. Тем самым обеспечивается продвижение по файлу.
Файл отсоединяется от определенного потока (то есть разрывается связь между файлом и потоком) с помощью операции закрытия файла. При закрытии файла, открытого с целью вывода, содержимое (если оно, конечно, есть) связанного с ним потока записывается на внешнее устройство. Этот процесс обычно называют дозаписью потока. При этом гарантируется, что никакая информация случайно не останется в буфере диска.
Если программа завершает работу нормально, то есть либо main() возвращает управление операционной системе, либо выход происходит через exit(), то все файлы закрываются автоматически.
В случае же аварийного завершения работы программы, например, в случа краха или завершения путем вызова abort(), файлы не закрываются.
У каждого потока, связанного с файлом, имеется управляющая структура, содержащая информацию о файле. Она имеет тип FILE. Блок управления файлом - это небольшой блок памяти, временно выделенный операционной системой для хранения информации о файле, который был открыт для использования. Блок управления файлом обычно содержит информацию об идентификаторе файла, его расположении на диске и указателе текущей позиции в файле.
Для выполнения всех операций ввода-вывода следует использовать только понятия потоков и применять всего лишь одну файловую систему. Ввод или вывод от каждого устройства автоматически преобразуется системой в легко управлемый поток. И это является достижением языка Си.
Таковы основополагающие замечания относительно существования различных потоков информации и связанных с ними файлов.
Файловая система языка Си состоит из нескольких взаимосвязанных между собой функций. Для их работы в Си требуется заголовочный файл <stdio.h> и такой же аналогичный ему заголовочный файл <iostream.h> требуется для работы в С++.
Ниже приведена таблица основных (часто используемых) функций файловой системы языка Си.
Имя | Что делает эта функция | Имя | Что делает эта функция |
fopen() | Открывает файл | feof() | Возвращает значение true (истина), если достигнут конец файла |
fclose() | Закрывает файл | ferror() | Возвращает значение true (истина), если произошла ошибка |
putc() | Записывает символ в файл | remove() | Стирает файл |
fputc() | То же, что и putc() | fflush() | Дозапись потока в файл |
getc() | Читает символ из файла | rewind() | Устанавливает указатель текущей позиции в начало файла |
fgetc() | То же, что и getc() | ftell() | Возвращает текущее значение указателя текущей позиции в файле |
fgets() | Читает строку из файла | fprintf() | Для файла то же, что printf() для консоли |
fputs() | Записывает строку в файл | fscanf() | Для файла то же, что scanf() для консоли |
fseek() | Устанавливает указатель текущей позиции на определенный байт файла |
Заголовок <stdio.h> представляет прототипы функций ввода-вывода в Си и определяет следующие три типа: size_t, fpos_t и FILE. Первые два: size_t, fpos_t представляют собой разновидности такого типа, как целое без знака. Отдельно рассмотрим третий тип: FILE.
Указатель файла - это то, что соединяет в единое целое всю систему ввода-вывода языка Си. Указатель файла - это указатель на структуру типа FILE. Он указывает на структуру, содержащую различные сведения о файле, например, его имя, статус, и указатель текущей позиции в начало файла. В сущности указатель файла определяет конкретный файл и используется соответствующим потоком при выполнении функции ввода-вывода.
Чтобы выполнять в файлах операции чтения и записи, программы должны использовать указатели соответствующих файлов. Чтобы объвить переменную-указатель файла необходимо использовать следующий оператор:
FILE *fp;
Функция fopen() открывает поток и связывает с этим потоком файл. Затем она возвращает указатель этого файла. Прототип функции имеет вид:
FILE *fopen(const char *имя_файла, const char *режим);
Здесь имя_файла - это указатель на строку символов, представляющую собой допустимое имя файла, в которое может входить спецификация файла (включает обозначение логического устройства, путь к файлу и собственно имя файла).
Режим - определяет, каким образом файл будет открыт. Ниже в таблице показаны допустимые значения режимов.
Режим | Что обозначает данный режим |
r | Открыть текстовый файл для чтения |
w | Создать текстовый файл для записи |
a | Добавить в конец текстового файла |
wb | Создать двоичный файл для записи |
rb | Открыть двоичный файл для чтения |
ab | Добавить в конец двоичного файла |
r+ | Открыть текстовый файл для чтения/записи |
w+ | Создать текстовый файл для чтения/записи |
a+ | Добавить в конец текстового файла или создать текстовый файл для чтения/записи |
r+b | Открыть двоичный файл для чтения/записи |
w+b | Создать двоичный файл для чтения/записи |
a+b | Добавить в конец двоичного файла или создать двоичный файл для чтения/записи |
Приведем фрагмент программы, в котором используется функция fopen() для открытия файла по имени TEST.
FILE *fp;
fp = fopen("test", "w");
Следует сразу же указать на недостаточность такого кода в программе. Хотя приведенный код технически правильный, но его обычно пишут немного по-другому.
FILE *fp;
if ((fp = fopen("test", "w")==NUL)
{
printf("Ошибка при открытии файла.\n\r")"
exit(1);
}
Рис. 1
Этот метод помогает при открытии файла обнаружить любую ошибку.
Например, защиту от записи или полный диск. Причем, обнаружить еще до того, как программа попытается в этот файл что-то записать. Поэтому всегда нужно вначале получить подтверждение, что функция fopen() выполнилась успешно, и лишь затем выполнять c файлом другие операции. Ниже на рисунке 1 приведена небольшую часть программы, которая. подтверждает или не подтверждает открытие файла. Результаты работы указанной программы приведены на рисунке 2.