Файлы - это то, без чего сложно представить любую серьезную программу. Ведь в реальности нам нужно не просто считать данные с клавиатуры и вывести на экран, а сохранить информацию, чтобы она осталась после завершения программы. Работа с файлами в 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() избавляет от головной боли с дублированием последних элементов.

Абсолютно бесплатно (лицензия LGPL)
Крутая кривая обучения — новичку нужно время
️