Запись - чтение информации

Закрытие файла

После окончания работы с файлом доступ к нему необходимо закрыть. Это выполняет функция fclose(указатель файла). Например, файл из предыдущего примера закрывается так: fclose (f);

Для закрытия нескольких файлов введена функция, объявленная следующим образом: void fcloseall(void);

Если требуется изменить режим доступа к файлу, то для этого сначала необходимо закрыть данный файл, а затем вновь его открыть, но с другими правами доступа. Для этого используют стандартную функцию:

FILE* freopen(char *имя_файла, char *режим, FILE *указатель_файла);

Эта функция сначала закрывает файл, объявленный «указателем_файла» (как это делает функция fopen), а затем открывает файл с «именем_файла» и доступом, задаваемым «режим».

В языке С имеется возможность работы с временными файлами, которые нужны только в процессе работы программы и которые надо удалить после выполнения части вычислений. В этом случае вместо fopen используется функция:

FILE* tmpfile(void);

которая создает на диске временный файл с правами доступа «w+b», после завершения работы программы или после закрытия временного файла он автоматически удаляется.

 

Все действия по чтению-записи данных в файл можно разделить на три группы:

- операции посимвольного ввода-вывода;

- операции построчного и форматированного ввода-вывода;

- операции ввода-вывода по блокам.

Рассмотрим основные функции, применяемые в каждой из указанных трех групп операций. Заметим, что при ошибке чтения/записи они, как правило, подобно fopen(), не прерывают работу программы, а вместо этого возвращают определеннное значение, сигнализирующее об ошибке. Это часто используют целенаправленно для определения конца файла: такое значение считается признаком достижения конца файла.

а) Посимвольный ввод-вывод

В функциях посимвольного ввода-вывода происходит прием одного символа (байта) из файла или передача одного символа в файл:

int fgetc(FILE *f) - считывает и возвращает символ из файла f;
int fputc(int ch, FILE *f) - записывает в файл f символ ch.

(Хотя ch формально имеет тип int, при успешной работе он преобразуется в (либо из) unsigned char. Но при ошибке эти функции возвращают стандартное значение EOF (обычно равное -1)).

б) Построчный и форматированный ввод-вывод

Эти функции служат для чтения/записи текста и обычно применяются для текстовых файлов.

В функциях построчного ввода-вывода происходит пере­нос из файла, или в файл строк символов.

 

 

char * fgets (char *S, int m, FILE *f) - чтение из файла f в массив S строки текста, длиной не более m байт. При ошибке (и только при ошибке) возвращает NULL. При чтении, в отличие от gets(), прочтенный символ '\n' заносится в массив S в конец полученной строки.
int fputs (char *S, FILE *f) - запись в файл f строки S. В отличие от puts(), в конце строки символ '\n' автоматически не добавляется; т.е. fputs(S,f) равносильно fprintf(f,"%s",S) (см. ниже).

Форматированный ввод-вывод производится функциями:

int fscanf(FILE *f, char *формат, список адресов объектов)   - считывает из файла f информацию для объектов в соответствии с указанными форматами;
int fprintf(FILE *f, char *формат, список объектов) - записывает в файл f объекты, указанные в списке в соответствии с форматами.

Данные функции аналогичны функциям scanf() и printf(), рассмотренным раньше, только добавлен параметр – указатель на файл. При ошибке возвращают значение, меньшее или равное 0.

 

в) Блоковый ввод-вывод

Функции блокового ввода-вывода читают из файла или записывают в файл заданное число байт без их преобразования. Другими словами, они способны записать в файл любую переменную или массив в том же двоичном представлении, в каком они хранятся в машинной памяти; а впоследствии таким же образом прочесть их оттуда. Поэтому они являются самым удобным средством сохранения на диск данных программы для их последующего прочтения этой же программой.

Недостатком подобного способа сохранения является то, что полученный файл не будет иметь текстовый формат, и читать из него данные в правильном виде способна лишь программа, «знающая» порядок и типы хранения в нем данных. Тем не менее, если хранимые данные содержат символы (тип char), то они обычно будут видны при просмотре «Блокнотом», часто вперемежку со специальными символами.

Блоковый ввод-вывод осуществляется следующими функциями:

int fread(void *p, int size, int n, FILE *f) - считывает n блоков по size байт каждый из файла f в область памяти с адресом p (необхо­димо заранее отвести память под считываемый блок);
int fwrite (void *p, int size, int n, FILE *f) - записывает n блоков по size байт каждый из области памяти с адресом p в файл f.  

В функциях блокового ввода-вывода количество байт задается несколько сложным образом: вводится термин «блок». Это – совершенно условная единица, под которой понимается любое заданное конкретное число байт. Программист должен указать как «размер блока», так и «число блоков»; реальное количество байт будет их произведением. Такой формат удобен при чтении/записи массива как целого.

Результатом этих функций является количество успешно прочтенных/записанных блоков. Оно равно n, либо меньше (при невозможности прочесть/записать n блоков). Неравенство результата значению n является признаком либо сбоя, либо достижения конца файла.

Например, если в файле хранится массив целых чисел, и его размер неизвестен (известно лишь, что он не превосходит 10000), то и весь массив, и его длину можно прочесть одной командой:

 

int a[10000],n;

n=fread(a, sizeof(int), 10000, f);

 

Здесь мы пытаемся прочесть в массив 10000 «блоков», где каждый блок – одно число типа int; реально будет прочтено столько чисел, сколько есть в файле, и это количество присвоено n.

 

Замечание. Функции блокового ввода-вывода трудно применить к переменной типа String, т.к. в типе String (см. тему «Строки») текст хранится отдельно от самой переменной, представляющей собой лишь указатель на этот текст. Если необходимо выполнить блоковый ввод-вывод структуры, содержащей текст, рекомендуется поэтому ее текстовые поля объявлять не как String, а как массив char.

 

г) Проверка достижения конца файла

Вместо вышеописанных способов определения «невозможности дальнейшего чтения», для проверки достижения конца файла можно пользоваться функцией feof() :

 

intfeof(FILE *f) ;

 

Она возвращает ненулевое значение, если до этого была совершена неудачная попытка чтения за концом файла. Таким образом, если feof() не равно нулю, то достигнут конец файла, и при этом последнее прочтенное из файла значение является ложным и должно быть проигнорировано.

 

Можно также использовать описанную ниже функцию filelength().

 

д) Сброс буфера файла

Заметим, что если после записи данных файл не был закрыт, часть «записанных» данных может не сохраниться. Это связано с тем, что данные вначале записываются в буфер файла, и лишь после его заполнения, либо закрытия файла – на диск. Если нужно, не закрывая файл, тем не менее гарантировать, что все операции ввода-вывода физически исполнены, можно вызвать функцию «сбросабуфера» файла fflush() :

 

int fflush(FILE *stream);

Она вызывает завершение всех действий, использующих буфер, после чего он становится пустым.