- Файловый ввод/вывод в языке Си
-
Язык программирования Си поддерживает множество функций стандартных библиотек для файлового ввода и вывода. Эти функции составляют основу заголовочного файла стандартной библиотеки языка Си
<потоками байтов, которые могут быть как "потоками ввода", так и "потоками вывода". В отличие от некоторых ранних языков программирования, язык Си не имеет прямой поддержки произвольного доступа к файлам данных; чтобы считать записанную информацию в середине файла, программисту приходится создавать поток, ищущий в середине файла, а затем последовательно считывать байты из потока.Потоковая модель файлового ввода-вывода была популяризирована во многом благодаря операционной системе Unix, написанной на языке Си. Большая функциональность современных операционных систем унаследовала потоки от Unix, а многие языки семейства языков программирования Си унаследовали интерфейс файлового ввода-вывода языка Си с небольшими отличиями (например, C++ отражает потоковую концепцию в своем синтаксисе (смотри
Содержание
Открытие файла при помощи fopen
Файл открывается при помощи
fopen, которая возвращает информацию потока ввода-вывода, прикрепленного к указанному файлу или другому устройству, с которого идет чтение (или в который идет запись). В случае неудачи функция возвращает нулевой указатель.Схожая функция
freopenбиблиотеки Си выполняет аналогичную операцию после первого закрытия любого открытого потока, связанного с ее параметрами.Они определяются как
FILE *fopen(const char *path, const char *mode); FILE *freopen(const char *path, const char *mode, FILE *fp);
Функция
fopenпо сути представляет из себя "обертку" более высокого уровня системного вызоваopenоперационной системы Unix. Аналогично,fcloseявляется оберткой системного вызова Unixclose, а сама структураFILEязыка Си зачастую обращается к соответстующему файловому дескриптору Unix. В fdopen может использоваться для инициализации структурыFILEфайловым дескриптором. Тем не менее, файловые дескрипторы как исключительно Unix-концепция не представлены в стандарте языка Си.Параметр
mode(режим) дляfopenиfreopenдолжен быть строковый и начинаться с одной из следующих последовательностей:режим описание начинает с.. r rb открывает для чтения начала w wb открывает для записи (создает файл в случае его отсутствия). Удаляет содержимое и перезаписывает файл. начала a ab открывает для добавления (создает файл в случае его отсутствия) конца r+ rb+ r+b открывает для чтения и записи начала w+ wb+ w+b открывает для чтения и записи. Удаляет содержимое и презаписывает файл. начала a+ ab+ a+b открывает для чтения и записи (добавляет в случае существования файла) конца Значение "b" зарезервировано для двоичного режима С. Стандарт языка Си описывает два вида файлов — текстовые и двоичные — хотя операционная система не требует их различать. Текстовый файл - файл, содержащий текст, разбитый на строки при помощи некоторого разделяющего символа окончания строки или последовательности (в Unix - одиночный символ перевода строки; в Microsoft Windows за символом перевода строки следует знак возврата каретки). При считывании байтов из текстового файла, символы конца строки обычно связываются (заменяются) с переводом строки для упрощения обработки. При записи текстового файла одиночный символ перевода строки перед записью связывается (заменяется) с специфичной для ОС последовательностью символов конца строки. Двоичный файл - файл, из которого байты считываются и выводятся в "сыром" виде без какого-либо связывания (подстановки).
При открытом файле в режиме обновления ( '+' в качестве второго или третьего символа аргумента обозначения режима) и ввод и вывод могут выполняться в одном потоке. Тем не менее, запись не может следовать за чтением без промежуточного вызова
fflushили функции позиционирования в файле (fseek,fsetposилиrewind), а чтение не может следовать за записью без промежуточного вызова функции позиционирования в файле. [1]Режимы записи и добавления пытаются создать файл с заданным именем, если такого файла еще не существует. Как указывалось выше, если эта операция оканчивается неудачей,
fopenвозвращаетЗакрытие потока при помощи fclose
Функция
fcloseпринимает один аргумент: указатель на структуруFILEпотока для закрытия.int fclose(FILE *fp);
Функция возвращает нуль в случае успеха и
Чтение из потока при помощи fgetc
Функция
fgetcприменяется для чтения символа из потока.int fgetc(FILE *fp);
В случае успеха,
fgetcвозвращает следующий байт или символ из потока (зависит от того, файл "двоичный" или "текстовый" (как выше обсуждалось). В противном случае,fgetcвозвращаетferrorилиfeofс указателем на файл.)Стандартный макрос
getcтакже определен в<stdio.h>, успешно работая какfgetc, кроме одного: будучи макросом, он может обрабатывать свои аргументы более одного раза.Стандартная функция
getcharтакже определена в<stdio.h>, она не принимает аргументов, и эквивалентнаgetc(stdin)."Ловушка" EOF
Распространенной ошибкой является использование
fgetc,getcилиgetcharдля присваивания результата переменной типаcharперед сравнением его сEOF. Следующий фрагмент кода демонстрирует эту ошибку, а рядом приведен корректный вариант:Ошибка Правильно char c; while ((c = getchar()) != EOF) { putchar(c); }
int c; while ((c = getchar()) != EOF) { putchar(c); }
Нужно учитывать систему, в которой тип
char, длина которого составляет 8 бит, представляет 256 различных значений.getcharможет возвращать любой из 256 возможных символов, а также может возвращатьEOFдля обозначения конца файла, т.е. всего 257 возможных возвращаемых значений.Когда результат
getcharприсваивается переменной типаchar, которая может прдставить лишь 257 различных значений, присходит вынужденная потеря информации - при сжатии 257 значений в 256 "мест" проиходит коллизия. ЗначениеEOFпри конвертации вcharстановится неотличимым от любого из остальных 256 символов. Если этот символ обнаружен в файле, код, приведенный выше, может принять его за признак конца файла, или, что еще хуже, если типchar- беззнаковый, тогда с учетом того, чтоEOF- значение отрицательное, оно никогда не сможет стать равным любому беззнаковомуchar, и таким образом, пример выше не закончится на метке конца файла, а будет выполняться вечно, повторно печатая символ, получающийся при конвертацииEOFвchar.В систамех, где
intиcharодинакового размера, даже "правильный" вариант будет работать некорректно из-за сходстваEOFи другого символа. Правильным вариантом обработки подобной ситуации является проверкаferrorпосле того, какgetcharвернетEOF. Еслиfeofопределит, что конец файла еще не достигнут, аferror"сообщит", что ошибок нет, тоEOF, возвращенныйgetcharможет считаться текущим символом. Такие дополнительные проверки делаются редко, так как большинство программистов считает, что их код никогда не будет выполняться на подобных системах с "большимchar". Другой способ состоит в использовании проверки при компиляции, чтоUINT_MAX > UCHAR_MAX, которая хотя бы предотвратит компиляцию на подобных системах.fwrite
В языке программирование Си функции
freadиfwriteсоответственно реализуют файловые операции ввода и вывода.freadиfwriteобъявлены в<Запись в файл при помощи fwrite
fwrite определяется как
int fwrite ( const void * array, size_t size, size_t count, FILE * stream );
Функция
fwriteзаписывает блок данных в поток. Таким образом запишется массив элементовcountв текущую позицию в потоке. Для каждого элемента запишетсяsizeбайт. Индикатор позиции в потоке изменится на число байт, записанных успешно. Возвращаемое значение будет равноcountв случае успешного завершения записи. В случае ошибки возвращаемое значение будет меньшеcount.Следующая программа открывает файл пример.txt, записывает в него строку символов, а затем его закрывает.
#include <stdio.h> #include <string.h> #include <stdlib.h> int main(void) { FILE *fp; size_t count; char const *str = "привет\n"; fp = fopen("пример.txt", "wb"); if(fp == NULL) { perror("ошибка открытия пример.txt"); return EXIT_FAILURE; } count = fwrite(str, 1, strlen(str), fp); printf("Записано %lu байт. fclose(fp) %s.\n", (unsigned long)count, fclose(fp) == 0 ? "успешно" : "с ошибкой"); return 0; }
Запись в поток при помощи fputc
Функция
fputcприменяется для записи символа в поток.int fputc(int c, FILE *fp);
Параметр
c"тихо" конвертируется вunsigned charперед выводом. Если прошло успешно, тоfputcвозвращает записанный символ. Если ошибка, тоfputcвозвращаетEOF.Стандартный макрос
putcтакже определен в<stdio.h>, работая в общем случае аналогичноfputc, за исключением того момента, что будучи макросом, он может обрабатывать свои аргументы более одного раза.Стандартная функция
putchar, также определенная в<stdio.h>, принимает только первый аргумент, и является эквивалентнойputc(c, stdout), гдеcявляется упомянутым аргументом.Пример использования
Нижеследующая программа на языке Си открывает двоичный файл с названием мойфайл, читает пять байт из него, а затем закрывает файл.
#include <stdio.h> #include <stdlib.h> int main(void) { char buffer[5] = {0}; /* инициализируем нулями */ int i, rc; FILE *fp = fopen("мойфайл", "rb"); if (fp == NULL) { perror("Ошибка при открытии \"мойфайл\""); return EXIT_FAILURE; } for (i = 0; (rc = getc(fp)) != EOF && i < 5; buffer[i++] = rc) ; fclose(fp); if (i == 5) { puts("Прочитанные байты..."); printf("%x %x %x %x %x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); } else fputs("Ошибка чтения файла.\n", stderr); return EXIT_SUCCESS; }
Смотри также
Дополнительные источники
- – страница помощи man в Linux
- – страница помощи man в Linux
- – страница помощи man в Linux
- – страница помощи man в Linux
- Вопрос 12.1 в вопросах-ответах по Си: использование
charдля хранения возвращаемогоgetcзначения
Wikimedia Foundation. 2010.