Ввод-вывод данных из файла (в файл)

Пример простейшей программы на C предполагал, что входные данные (последовательность целых чисел) подавались через поток стандартного ввода программы. При обычном запуске программы этот потк связан с клавиатурой терминала, то есть все данные, поступающие в программу, должны быть набраны пользователем на клавиатуре. Это не всегда удобно. Здесь мы рассмотрим, как можно считывать данные из заранее подготовленного файла.

Предположим, что входные данные, которые мы должны обработать записаны в файле с именем "input.txt". Приведенный ниже фрагмент программы открывает файл с этим именем на чтение, считывает одно целое число и закрывает файл. Содержательная обработка данных не рассматривается.

#include <stdio.h>

int main() {
    FILE *inp; /* информация о файле */
    int current = 0;

    inp = fopen("input.txt", "r");
    if(inp == NULL) {
        /* Не удалось открыть файл */
        return -1;
    }

    /* Считываем первый элемент последовательности ИЗ ФАЙЛА */
    if(fscanf(inp, "%d", &current) != 1 || current == 0) {
        printf("Не удалось прочитать первый элемент\n");
        return -1;
    }

    /* ... Здесь мы обрабатываем данные ... */

    fclose(inp);  /* Закрываем файл */
    return 0;
}

Стандартная схема работы: открыть файл, прочитать или записать данные, закрыть файл. Рассмотрим эти действия более подробно.

Для возможности работы с каким-либо файлом необходимо объявить файловую переменную. В приведенном примере это переменная inp. В файловой переменной будет храниться служебная информация о файле. Эта информация заполняется и обновляется автоматически при вызове функций работы с файлами. Нашей программе необходимо только создать эту перемнную.

Файловая переменная сама по себе не свзана с файлом. Для работы с конкретным файлом, например, с файлом "input.txt", этот файл необходимо открыть. Файл может быть открыт для чтения (мы планируем только считывать информацию), записи (мы планируем выводить в него информацию) или одновременно для чтения и записи. Последний случай требует более глубокого понимания устройства файлов и здесь не рассматривается.

Открыть файл можно вызовом функции fopen. Заметим, что практически все функции по работе с файлами начинаются с быквы f. fopen является сокращением от file open. Функция fopen имеет два параметра. Первый – это имя файла, который необходимо открыть. Второй параметр – строка, определющая режим, в котором указанный файл необходимо открыть. Самыми простыми значениями второго параметра являются "r" и "w", которые означают открытие файла на чтение и запись, соответсвенно. В нашем примере для открытия файла использовался такой вызов:

inp = fopen("input.txt", "r");
/* открыть файл "input.txt" на чтение */

При успешном выполнении операции функция fopen возвращает файловую переменную, которая может использоваться при вызове последующих функций по работе с файлами. Если "что-то пошло не так", например, на диске нет файла с указанным именем или с этим файлом запрещено выполнять запрашиваемое действие (файл защищен от записи, а мы пытаемся его открыть на запись), то функция fopen возвращает специальное значение NULL. Любое другое возвращаемое значение является корректной файловой переменной. Это позволяет выявлять ситуации, когда при открытии файла возникла ошибка.

Обязательно проверяйте значение функции fopen на NULL. Если файловая переменная имеет значение NULL и передается в какую-либо другую функцию работы с файлами, то это приводит к аварийному завершению программы.
inp = fopen("input.txt", "r");  /* открыть файл "input.txt" на чтение */
if(inp == NULL) {
    /* Не удалось открыть файл */
    return -1;
}

Считывание данных из файла выполяется функцией fscanf (с первой буквой f, которая означает, что эта функция работает с файлом). Отличие от scanf состоит том, что первым аргументом является файловая переменная:

if(fscanf(inp, "%d", &current) != 1) {
    /* не удалось прочитать число из файла imp */
}

После того, как все данные из файла были прочитаны, файл нужно закрыть. Операционная система устанавливает верхний предел на число файлов, которые могут быть одновременно открыты программой. Если Ваша программа не будет закрывать файлы, то возможна ситуация, когда новые файлы уже не удастся открывать. Например, приведенный ниже фрагмент программы пытается 10000 раз открыть один и тот же файл. Если не вызывать функцию fclose, то сообщение об ошибке будет напечатано.

FILE *inp;
int k;
for(k = 0; k <= 10000; k++) {
    inp = fopen("input.txt", "r");  /* открыть файл "input.txt" на чтение */
    if(inp == NULL) {
        printf("Не удалось открыть файл! k=%d\n", k);
        break;
    }
    /* забыли fclose(imp); */
}

Вывод в файл

Вывод в файл выполняется аналогично. Ниже приводится программа, которая записывает в файл значение переменной. Для наглядности корректность выполнения операций не проверяется.

#include <stdio.h>

int main() {
    FILE *out; /* информация о файле */
    int current = 1234;

    out = fopen("input.txt", "w"); /* открываем НА ЗАПИСЬ ("w")! */
    fprintf(out, "Значение current=%d\n", current);
    fclose(out); /* закрываем файл */
    return 0;
}

Стандартые потоки ввода-вывода

В языке C есть три стандартных потока вывода (три файловых переменных, которые всегда соответствуют заранее открытым файлам): stdin, stdout, stderr. Это переменные объявлены в заголовочном файле <stdio.h> и для того, чтобы ими пользоваться, не нужно делать fopen и fclose.

stdin — поток стандартного ввода (обычно это ввод с клавиатуры). Вызов scanf("%d", &var) эквивалентен fscanf(stdin, "%d", &var).

stdout — поток стандартного вывода (обычно это вывод на экран). Вызов printf("%d", var) эквивалентен fprintf(stdout, "%d", var).

stderr — поток вывода для сообщений об ошибках (обычно это вывод на экран). Пример: fprintf(stderr, "Ошибка! Ошибка!! Значение var=%d!!!\n", var). По техническим причинам, которые должны быть объяснены отдельно, сообщения об ошибках лучше выводить именно таким образом.