Перейти к содержанию
Educora
Продвинутый22 мин8 / 8

Файлы и проект: записи об учениках

Записывай данные в файлы и читай их с помощью fopen, fprintf, fgets и fclose, а затем объедини структуры, динамическую память и файлы в программе учёта учеников.

Проверь себя
В этом уроке ты узнаешь
  • Открывать файл в нужном режиме через 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.

C
#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. 1
    Подготовь тип и запись

    Определи структуру Student и напиши функцию save, которая записывает массив в файл через fprintf.

  2. 2
    Загрузи файл

    load сначала считает строки, выделяет через malloc ровно столько места, сколько нужно, возвращается в начало файла через rewind и разбирает каждую строку через sscanf.

  3. 3
    Выведи отчёт и освободи память

    main записывает учеников, загружает их обратно, находит средний балл и лучший результат и в конце вызывает free.

C
#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;
}
Часть 1: тип и функция save. При успехе она возвращает 1, при ошибке — 0.
Text
Aysel 92
Murad 78
Leyla 85
Elvin 64
Содержимое файла students.txt после save.

sscanf работает как scanf, но читает не с клавиатуры, а из строки и возвращает количество успешно прочитанных значений. %19s читает не больше 19 символов — вместе с '\0' это ровно помещается в 20-байтовый массив name. Строка с неверным форматом просто пропускается.

C
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;
}
Часть 2: load возвращает указатель на новый массив, а количество элементов записывает через указатель count.

Чтобы выровнять столбцы отчёта, укажем ширину: %-6s выравнивает имя по левому краю в поле из 6 символов, а %3d — число по правому краю в поле из 3 символов.

C
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
Часть 3. Помести три части по порядку в один файл 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.

1 / 10
Что возвращает fopen, если файл не удалось открыть?