Работа с файлами в C: от открытия до закрытия
-
Файлы - это то, без чего сложно представить любую серьезную программу. Ведь в реальности нам нужно не просто считать данные с клавиатуры и вывести на экран, а сохранить информацию, чтобы она осталась после завершения программы. Работа с файлами в C может показаться запутанной, но на самом деле это логично и понятно, если разобраться. И особенно это необходимо если мы работаем в промышленности и со станками ЧПУ.
Работа с файлами строится на трех китах:
- Открыть файл - сообщить операционной системе, что нам нужен доступ к файлу (для чтения, записи или того и другого)
- Выполнить операции - читать или записывать данные
- Закрыть файл - освободить ресурсы и сохранить изменения
Пропустить третий шаг нельзя - если не закрыть файл, данные могут не записаться, а сам файл останется заблокированным.
Объект FILE и функция fopen
Для работы с файлами в C используется структура FILE. Это абстрактный объект, который хранит всю информацию о файловом потоке: указатель на буфер, позицию в файле, индикаторы состояния.
FILE *file; file = fopen("test.txt", "w");Функция
fopen()принимает два параметра:- Путь к файлу - может быть абсолютным (
C:/projects/data.txt) или относительным (data.txt) - Режим доступа - строка, определяющая, что мы собираемся делать с файлом
Режимы открытия файлов
Режимы доступа определяют, как мы будем работать с файлом:Режим Описание “r” Чтение. Файл должен существоватьlearnc+1 “w” Запись. Если файл существует — его содержимое удаляетсяlearnc+1 “a” Добавление в конец. Файл создаётся, если не существовалlearnc “r+” Чтение и запись. Файл должен существоватьlearnc “w+” Чтение и запись. Старое содержимое теряетсяlearnc “a+” Чтение и добавление в конецlearnc Для бинарных файлов добавляем букву
b:"rb","wb","ab+"и так далее. В текстовом режиме можно использовать"rt","wt", хотя по умолчанию и так используется текстовый режим.Простой пример: запись и чтение
Перед тем как приступить к написанию новой программы надеюсь вы уже знаете как делать компиляцию, да и в целом познакомились с базовыми правилами Си. Если нет, то лучше прочитайте и пройдитесь по основам тут. Этих базовых знаний будет не достаточно, материал скорее подготовлен для более опытных программистов чем для новичков, но Вам в любом случае рано или поздно придется столкнуться с файлами.
Продолжаем, теперь давайте запишем строку в файл, а потом считаем ее обратно:
#include <stdio.h> int main() { FILE *file; char buffer[128]; // Записываем данные file = fopen("test.txt", "w"); fprintf(file, "Hello, World!"); fclose(file); // Читаем данные file = fopen("test.txt", "r"); fgets(buffer, 127, file); printf("%s", buffer); fclose(file); return 0; }Здесь
fprintf()иfgets()работают точно так же, какprintf()иgets(), только первым параметром передаётся указатель на файл.Обработка ошибок при открытии
Если файл не удалось открыть,
fopen()вернетNULL. Это может случиться по разным причинам: файла не существует, нет прав доступа, диск переполнен. Проверка на ошибки обязательна:file = fopen("data.txt", "r"); if (file == NULL) { printf("Ошибка открытия файла!\n"); exit(1); }При работе с несколькими файлами важно закрывать уже открытые файлы, если один из них не открылся:
inputFile = fopen("input.txt", "r"); if (inputFile == NULL) { printf("Ошибка открытия input.txt\n"); exit(1); } outputFile = fopen("output.txt", "w"); if (outputFile == NULL) { printf("Ошибка открытия output.txt\n"); if (inputFile != NULL) { fclose(inputFile); // Закрываем первый файл! } exit(1); }
Буферизация: почему данные не сразу попадают в файл
Когда мы записываем данные в файл, они сначала попадают в буфер - специальную область памяти. Данные из буфера записываются в файл:
- Когда буфер заполнен
- Когда файл закрывается функцией
fclose() - Когда программа завершается успешно
- Когда мы явно вызываем
fflush(file)
Пример с принудительной очисткой буфера:
FILE *file = fopen("test.txt", "w"); char c; do { c = getch(); fprintf(file, "%c", c); fprintf(stdout, "%c", c); fflush(file); // Данные сразу записываются в файл } while(c != 'q'); fclose(file);Без вызова
fflush()данные останутся в буфере до тех пор, пока он не заполнится или файл не закроется.Проблема с
feof()Функция
feof()проверяет, достигнут ли конец файла. Звучит полезно, но есть подвох: часто она работает некорректно и дублирует последний считанный элемент:// Плохой пример последний символ выведется дважды! while (!feof(input)) { fscanf(input, "%c", &c); fprintf(stdout, "%c", c); }Правильное решение - использовать возвращаемое значение функций чтения. Например,
fscanf()возвращает количество успешно прочитанных элементов:// Правильный пример while (fscanf(input, "%c", &c) == 1) { fprintf(stdout, "%c", c); }Или можно использовать EOF для посимвольного чтения:
int ch; // int, а не char! while ((ch = fgetc(input)) != EOF) { printf("%c", ch); }Обратите внимание: переменная должна быть типа int, потому что EOF - это целочисленная константа, а не символ.
Стандартные потоки: stdin, stdout, stderr
В любой программе автоматически открываются три стандартных потока:
stdin- стандартный ввод (клавиатура)stdout- стандартный вывод (консоль)stderr- поток вывода ошибок (тоже консоль, но можно перенаправить отдельно)
Это обычные файловые потоки типа
FILE*, с которыми можно работать черезfprintf()иfscanf():int a, b; fprintf(stdout, "Введите два числа\n"); fscanf(stdin, "%d", &a); fscanf(stdin, "%d", &b); if (b == 0) { fprintf(stderr, "Ошибка: деление на ноль\n"); } else { fprintf(stdout, "%.3f\n", (float)a / (float)b); }
Практические примеры
Копирование файла посимвольно:
FILE *origin = fopen("input.txt", "r"); FILE *copy = fopen("output.txt", "w"); if (origin == NULL || copy == NULL) { printf("Ошибка открытия файла\n"); if (origin) fclose(origin); if (copy) fclose(copy); exit(1); } int ch; while ((ch = fgetc(origin)) != EOF) { fputc(ch, copy); } fclose(origin); fclose(copy);Поиск максимального числа в файле:
FILE *input = fopen("numbers.txt", "r"); if (input == NULL) { printf("Ошибка открытия файла\n"); exit(1); } int num, maxn = INT_MIN; while (fscanf(input, "%d", &num) == 1) { if (num > maxn) { maxn = num; } } printf("Максимальное число: %d\n", maxn); fclose(input);Подсчет строк в файле:
int countLines(const char *filename) { FILE *f = fopen(filename, "r"); if (f == NULL) return -1; int lines = 0; int ch; while ((ch = fgetc(f)) != EOF) { if (ch == '\n') { lines++; } } fclose(f); return lines; }
Резюмируем
Работа с файлами в C требует дисциплины: нужно всегда проверять результат
fopen(), правильно выбирать режим доступа и обязательно закрывать файлы. Понимание буферизации помогает избежать потери данных. А правильное использование функций чтения вместоfeof()избавляет от головной боли с дублированием последних элементов.
© 2022 - 2025 InvestSteel, Inc. Все права защищены.