- Открывать файл в нужном режиме через
fopen, проверятьNULLи закрывать его черезfclose - Записывать в файл через
fprintfи читать его построчно черезfgets - Извлекать данные из прочитанной строки с помощью
sscanf - Объединять структуры, динамическую память и файлы в одной программе
У всех программ, которые ты писал до сих пор, есть общий недостаток: как только программа завершается, все данные исчезают. Оценки в журнале, рекорд в игре, настройки — всё, что понадобится завтра, нужно записать в файл. На этом уроке ты научишься работать с файлами в C, а затем объединишь главные темы курса — структуры, указатели, динамическую память и файлы — в одном проекте.
Открытие и закрытие файла
В C с файлом работают через указатель типа FILE *. fopen(имя, режим) открывает файл и возвращает этот указатель, а если открыть не удалось — например, файла нет или нет прав на запись в папку, — возвращает NULL. Закончив работу, файл закрывают через fclose: она записывает на диск данные, которые ещё ждут в памяти, и возвращает ресурс системе.
| Режим | Значение | Если файла нет |
|---|---|---|
"r" | чтение | fopen возвращает NULL |
"w" | запись; старое содержимое стирается | создаётся новый файл |
"a" | дозапись в конец | создаётся новый файл |
fprintf работает как printf, только первым аргументом получает файл. fgets(строка, размер, файл) читает из файла одну строку: не больше размер − 1 символов, добавляет в конец '\0' и оставляет '\n' внутри строки. В конце файла она возвращает NULL, поэтому её удобно использовать в цикле while.
#include <stdio.h>
int main(void) {
FILE *f = fopen("notes.txt", "w");
if (f == NULL) {
printf("Cannot open file\n");
return 1;
}
fprintf(f, "C was created in %d\n", 1972);
fprintf(f, "Files keep data\n");
fclose(f);
char line[100];
f = fopen("notes.txt", "r");
if (f == NULL) {
return 1;
}
while (fgets(line, sizeof line, f) != NULL) {
printf("Read: %s", line);
}
fclose(f);
return 0;
}Read: C was created in 1972 Read: Files keep data
printf("Read: %s", line) нет \n, потому что fgets оставила его внутри строки.Проект: записи об учениках
Учитель хочет хранить результаты теста. Наша программа запишет учеников в файл, затем прочитает его, загрузит учеников в динамический массив, не зная заранее их количества, и напечатает отчёт. Файл имеет простой текстовый формат: в каждой строке имя и балл.
- 1Подготовь тип и запись
Определи структуру
Studentи напиши функциюsave, которая записывает массив в файл черезfprintf. - 2Загрузи файл
loadсначала считает строки, выделяет черезmallocровно столько места, сколько нужно, возвращается в начало файла черезrewindи разбирает каждую строку черезsscanf. - 3Выведи отчёт и освободи память
mainзаписывает учеников, загружает их обратно, находит средний балл и лучший результат и в конце вызываетfree.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[20];
int score;
} Student;
int save(const char *path, const Student *list, int n) {
FILE *f = fopen(path, "w");
if (f == NULL) {
return 0;
}
for (int i = 0; i < n; i++) {
fprintf(f, "%s %d\n", list[i].name, list[i].score);
}
fclose(f);
return 1;
}save. При успехе она возвращает 1, при ошибке — 0.Aysel 92
Murad 78
Leyla 85
Elvin 64students.txt после save.sscanf работает как scanf, но читает не с клавиатуры, а из строки и возвращает количество успешно прочитанных значений. %19s читает не больше 19 символов — вместе с '\0' это ровно помещается в 20-байтовый массив name. Строка с неверным форматом просто пропускается.
Student *load(const char *path, int *count) {
FILE *f = fopen(path, "r");
if (f == NULL) {
return NULL;
}
char line[64];
int n = 0;
while (fgets(line, sizeof line, f) != NULL) {
n++;
}
Student *list = malloc(n * sizeof *list);
if (list == NULL) {
fclose(f);
return NULL;
}
rewind(f);
int i = 0;
while (i < n && fgets(line, sizeof line, f) != NULL) {
if (sscanf(line, "%19s %d", list[i].name, &list[i].score) == 2) {
i++;
}
}
fclose(f);
*count = i;
return list;
}load возвращает указатель на новый массив, а количество элементов записывает через указатель count.Чтобы выровнять столбцы отчёта, укажем ширину: %-6s выравнивает имя по левому краю в поле из 6 символов, а %3d — число по правому краю в поле из 3 символов.
int main(void) {
Student group[] = {{"Aysel", 92}, {"Murad", 78}, {"Leyla", 85}, {"Elvin", 64}};
if (!save("students.txt", group, 4)) {
printf("Cannot save\n");
return 1;
}
int n = 0;
Student *list = load("students.txt", &n);
if (list == NULL) {
printf("Cannot load\n");
return 1;
}
int sum = 0, best = 0;
for (int i = 0; i < n; i++) {
printf("%-6s %3d\n", list[i].name, list[i].score);
sum += list[i].score;
if (list[i].score > list[best].score) best = i;
}
printf("Students: %d\n", n);
printf("Average: %.2f\n", (double) sum / n);
printf("Best: %s\n", list[best].name);
free(list);
return 0;
}Aysel 92 Murad 78 Leyla 85 Elvin 64 Students: 4 Average: 79.75 Best: Aysel
records.c и скомпилируй — результат будет таким. (92 + 78 + 85 + 64) / 4 = 79,75.Главное
FILE *f = fopen(имя, режим);— если результатNULL, файл не открылся; в конце —fclose(f)."r"— чтение,"w"— запись со стиранием,"a"— дозапись в конец.fprintfпишет в файл форматированный текст;fgetsбезопасно читает строку и в конце файла возвращаетNULL.sscanfизвлекает значения из прочитанной строки; ограничение ширины вроде%19sзащищает от переполнения.- Настоящие программы сочетают структуры, динамическую память и файлы — завершай каждый
mallocвызовомfree.
Проверь себя
Вопросов: 10. Каждый правильный ответ приносит XP.
fopen, если файл не удалось открыть?