6.2 Основы файловой системы
Файловая система языка С состоит из нескольких взаимосвязанных функций. Самые распространенные из них показаны в таблице 9.1. Они объявлены в заголовочном файле <stdio.h>.
Таблица 6.1. Часто используемые функции файловой системы С
Имя |
Действие |
fopen() |
Открывает файл |
fclose() |
Закрывает файл |
fseek() |
Устанавливает указатель записи/чтения файла в определённую позицию |
ftell() |
Возвращает текущее значение указателя текущей позиции в файле |
fprintf() |
Для файла то же, что printf() для консоли |
fscanf() |
Для файла то же, что scanf() для консоли |
feof() |
Возвращает значение true (истина), если достигнут конец файла |
ferror() |
Возвращает значение true, если произошла ошибка |
remove() |
Стирает файл |
fflush() |
Дозапись потока в файл |
Указатель файла (файловая переменная) – это то, что соединяет в единое целое всю систему ввода/вывода языка С. Указатель файла – это указатель на структуру типа FILE. Он указывает на структурную переменную, содержащую различные сведения о файле, например, его имя, статус и позицию курсора файла. В сущности, указатель файла определяет конкретный файл и используется соответствующим потоком при выполнении функций ввода/вывода. Чтобы выполнять в файлах операции чтения и записи, программы должны использовать указатели соответствующих файлов. Пример объявления указателя файла (или файловой переменной):
FILE *fp;
Открытие файла
Функция fopen() открывает поток и связывает с этим потоком определенный файл. Затем она возвращает указатель этого файла. Чаще всего под файлом подразумевается дисковый файл. Прототип функции fopen() такой:
FILE* fopen ( const char *имя_файла, const char *режим_доступа );
где первый параметр имя_файла – это указатель на строку символов, представляющую собой допустимое имя файла, которое может также включать путь к этому файлу, а второй параметр режим_доступа определяет, каким образом файл будет открыт. В таблице 6.2 показаны режимы работы с файлом.
Таблица 6.2. Режимы работы с файлом
Режим |
Что означает |
r |
Открыть текстовый файл для чтения |
w |
Создать текстовый файл для записи |
a |
Добавить в конец текстового файла |
rb |
Открыть двоичный файл для чтения |
wb |
Создать двоичный файл для записи |
ab |
Добавить в конец двоичного файла |
r+ |
Открыть текстовый файл для чтения/записи |
w+ |
Создать текстовый файл для чтения/записи |
a+ |
Добавить в конец текстового файла или создать текстовый файл для чтения/записи |
r+b |
Открыть двоичный файл для чтения/записи |
w+b |
Создать двоичный файл для чтения/записи |
a+b |
Добавить в конец двоичного файла или создать двоичный файл для чтения/записи |
Хотя название большинства файловых режимов объясняет их смысл, сделаем некоторые дополнения. Если попытаться открыть файл только для чтения, а он не существует, то работа fopen() завершится отказом. А если попытаться открыть файл в режиме дополнения, а сам этот файл не существует, то он просто будет создан. Более того, если файл открыт в режиме дополнения, то все новые данные, которые записываются в него, будут добавляться в конец файла. Содержимое, которое хранилось в нем до открытия (если только оно было), изменено не будет. Далее, если файл открывают для записи, но выясняется, что он не существует, то он будет создан. А если он существует, то содержимое, которое хранилось в нем до открытия, будет утеряно, причем будет создан новый файл.
Разница между режимами r+ и w+ состоит в том, что если файл не существует, то в режиме открытия r+ он создан не будет, а в режиме w+ все произойдет наоборот: файл будет создан. Более того, если файл уже существует, то открытие его в режиме w+ приведет к утрате его содержимого, а в режиме r+ оно останется нетронутым.
Динамическая переменная с информацией о файле, которую создаёт и инициализирует функция fopen() и на которую указывает файловая переменная (или указатель файла), имеет следующую структуру:
typedef struct {
unsigned char *curp; // Current active pointer
unsigned char *buffer; // Data transfer buffer
int level; // Fill/Empty level of buffer
int bsize; // Buffer size
unsigned short istemp; // Temporary file indicator
unsigned short flags; // File status flags
wchar_t hold; // Ungetc char if no buffer
char fd; // File descriptor
unsigned char token; // Used for validity checking
} FILE;
В функции fopen() можно задать конкретное имя файла или предоставить выбор имени пользователю.
1) задание конкретного имени файла:
FILE *fp;
fp = fopen ( "file.txt", "wt" );
2) имя файла выбирает пользователь:
FILE *fp;
char FileName[16];
printf ( "\n Enter file name " );
scanf ( "%s", FileName ); // или scanf( "%s", &FileName[0] );
fp = fopen ( FileName, "wt" );
Функция fopen() возвращает указатель файла. Никогда не следует изменять значение этого указателя в программе. Если при открытии файла происходит ошибка, то fopen() возвращает пустой (NULL) указатель.
Хотя предыдущий код технически правильный, но его обычно пишут немного по-другому:
FILE *fp;
fp = fopen("file.txt", "wt");
if ( fp == NULL ) {
printf ( "\n Error opening the file \n");
exit(1);
}
Этот метод помогает обнаружить любую ошибку при открытии файла, например, защиту от записи или полный диск, причем обнаружить еще до того, как программа попытается в этот файл что-либо записать. Вообще говоря, всегда нужно вначале получить подтверждение, что функция fopen() выполнилась успешно, и лишь затем выполнять с файлом другие операции.
Максимальное число одновременно открытых файлов определяется константой FOPEN_MAX. Это значение не меньше 8, но чему оно точно равняется – это должно быть написано в документации по компилятору.
Закрытие файла
Функция fclose() закрывает поток, который был открыт с помощью вызова fopen(). Функция fclose() записывает в файл все данные, которые еще оставались в дисковом буфере, и проводит официальное закрытие файла на уровне операционной системы. Отказ при закрытии потока влечет всевозможные неприятности, включая потерю данных, испорченные файлы и возможные периодические ошибки в программе. Функция fclose() также освобождает блок управления файлом, связанный с этим потоком, давая возможность использовать этот блок снова. Так как количество одновременно открытых файлов ограничено, то, возможно, придется закрывать один файл, прежде чем открывать другой. Прототип функции fclose():
int fclose (FILE *уф);
где уф – указатель файла, возвращенный в результате вызова fopen(). Возвращение нуля означает успешную операцию закрытия. В случае же ошибки возвращается EOF. Чтобы точно узнать, в чем причина этой ошибки, можно использовать стандартную функцию ferror() (о которой вскоре пойдет речь). Обычно отказ при выполнении fclose() происходит только тогда, когда диск был преждевременно удален (стерт) с дисковода или на диске не осталось свободного места.
