Функции библиотеки C -feof()

Стандартная библиотека C - <stdio.h>

feof()Используется для определения, был ли файл уже прочитан до конца.

Мы можем представить её как «детектор конца» — каждый раз, когда вы заканчиваете читать часть содержимого, вы спрашиваете у неё: «Мы достигли конца?»


Описание

Функции библиотеки Cint feof(FILE *stream)Проверяет индикатор конца файла для заданного потока.

Здесь появляются два новых понятия; сначала объясню их по отдельности:

ПотокЭто абстракция ввода/вывода в языке C. Будь то файл на жёстком диске, ввод с клавиатуры или сетевые данные, в C всё это единообразно рассматривается как «поток». Вы можете представить поток как водопроводную трубу, по которой данные текут с одного конца к другому.

Индикатор конца файла (маркер EOF)Это внутренний флаг состояния, который несёт каждый файловый поток. Он похож на закладку, которая автоматически переворачивается на страницу «чтение завершено», когда содержимое файла полностью прочитано.

Назначение feof() — проверить эту закладку: если закладка показывает «чтение завершено», feof() возвращает ненулевое значение. Если закладка всё ещё на «чтение не завершено», feof() возвращает 0.


Объявление

Ниже приведено объявление функции feof(); давайте разберём его значение по частям:

int feof(FILE *stream)

intЭто тип возвращаемого значения — feof() возвращает целое число; ненулевое значение означает, что конец достигнут, 0 означает, что нет.

FILE *streamЭто параметр — нужно передать указатель на файл (указатель, возвращаемый fopen() при открытии файла), чтобы сообщить feof(), какой файловый поток проверять.


Параметр

feof() принимает один параметр:

ПараметрTypeОписание
streamFILE *Указатель на файл, возвращаемый функцией fopen(), указывающий на открытый файл, который нужно проверить.

Возвращаемое значение

Возвращаемое значение описывается следующим образом:

Возвращаемое значениеЗначение
ненулевое значениеФайл прочитан до конца; больше нет содержимого для чтения.
0Файл ещё не прочитан полностью, поэтому чтение можно продолжить

Ключевой момент: feof() возвращает ненулевое значение только после "попытки прочитать за концом файла". Если вы только что прочитали последний символ, но ещё не пытались продолжить чтение, feof() по-прежнему возвращает 0. Этот нюанс с моментом будет подробно объяснён в следующем разделе.


Пример

В следующем примере демонстрируется правильное использование функции feof().

Программа читает содержимое файла символ за символом и каждый раз после чтения символа использует feof(), чтобы проверить, достигнут ли конец.

Пример

#include <stdio.h>

int main ()
{
   // FILE * — это тип указателя на файл, используемый для указания на открытый файл
   FILE *fp;
   // fgetc() возвращает int вместо char, потому что ей может потребоваться вернуть EOF (-1)
   int c;

   // fopen() открывает файл, "r" указывает режим только для чтения (чтение)
   fp = fopen("file.txt","r");
   // Если файл не существует или доступ запрещён, fopen() возвращает NULL
   if(fp == NULL)
   {
      // perror() выводит системные сообщения об ошибках, помогая локализовать проблему
      perror("Ошибка файла");
      return(-1);
   }

   // Используйте бесконечный цикл + внутренние проверки
   // Это обеспечивает принцип "сначала прочитать, затем проверить", избегая чтения лишнего содержимого
   while(1)
   {
      // Шаг 1: Считать символ из файла
      c = fgetc(fp);
      // Шаг 2: Сразу после чтения используйте feof(), чтобы проверить, достигнут ли конец
      if( feof(fp) )
      {
          // Если достигнут конец, выйдите из цикла и прекратите обработку c
          break ;
      }
      // Шаг 3: Используйте значение c только после подтверждения, что конец не достигнут
      printf("%c", c);
   }

   // fclose() закрывает файл и освобождает системные ресурсы
   fclose(fp);
   return(0);
}

Предположим, у нас есть текстовый файлfile.txt, содержимое которого выглядит так:

这里是 example.com

Скомпилируйте и запустите приведённую выше программу, вывод будет следующим:

这里是 example.com

Программа правильно вывела всё содержимое файла, без каких-либо лишних искажённых символов.


Распространённая ошибка: почему нельзя помещать feof() в условие цикла

Многие новички пишут код в следующем стиле, который выглядит более кратким, но на самом деле неверен:

Неправильный пример

#include <stdio.h>

int main ()
{
   FILE *fp;
   int c;

   fp = fopen("file.txt","r");
   if(fp == NULL)
   {
      perror("Ошибка файла");
      return(-1);
   }

   // Неправильно: использование feof() в качестве условия входа в цикл
   while( !feof(fp) )
   {
      c = fgetc(fp);
      printf("%c", c);
   }

   fclose(fp);
   return(0);
}

Вывод этого неправильного кода будет таким:

这里是 example.com

В конце есть лишний искажённый символ. Почему так? Давайте ниже шаг за шагом проследим процесс выполнения цикла:

Итерации циклаПроверка feof() при входе в циклfgetc() читаетВывод printf()Описание
Раунд 1Возвращает 0 (ещё не прочитано)'这'这нормальный
Раунд 2Возвращает 0 (ещё не достигнут конец)'里'里нормальный
............Средние символы выводятся нормально
Последняя итерацияВозвращает 0 (ещё не достигнут конец)'m'mПоследний нормальный символ
Лишняя итерацияВозвращает 0 (Примечание! В этот момент 'm' только что прочитан, и feof ещё не знает, что достигнут конец.)EOF(-1)(искажённый текст)Ошибка! Считывание произошло лишний раз

Корень проблемы в том, что когда вы только что прочитали 'm' (последний символ файла), feof() всё ещё возвращает 0.

Он должен дождаться, пока вы «попытаетесь прочитать ещё раз» и обнаружите, что больше нечего читать, и только тогда он переключит внутренний флаг в состояние «ended».

Итак, шаблон while(!feof(fp)) заставляет цикл выполнить одну лишнюю итерацию. На этой лишней итерации fgetc() возвращаетEOF(-1), printf пытается вывести -1 как символ, что приводит к искажённому выводу.

Одно предложение, которое стоит запомнить:feof() — это «отчёт постфактум», а не «предупреждение заранее»EOF. Он отвечает на вопрос, «был ли достигнут конец во время последнего чтения», а не «будет ли достигнут конец при следующем чтении». Поэтому никогда не используйте feof() в качестве условия входа в цикл.

Правильный шаблон — это подход из более раннего примера: сначала читать, затем проверять и использовать прочитанное содержимое только после подтверждения, что оно безопасно.


Когда использовать feof()

feof() в основном используется в следующих двух сценариях:

СценарииИспользованиеПример
Чтение файла в циклеСначала читать, затем проверять (рекомендуется)while(1) { c = fgetc(fp); if(feof(fp)) break; ... }
Диагностика ошибок чтенияРазличение «нормального завершения» и «ошибки чтения»if(feof(fp)) { /* чтение успешно завершено */ } else if(ferror(fp)) { /* ошибка чтения */ }

Во втором сценарии возврат EOF функцией fgetc() имеет две возможности: либо содержимое действительно было полностью прочитано (определяется с помощью feof()), либо во время чтения произошла ошибка (определяется с помощью ferror()). Эти две ситуации необходимо различать и обрабатывать отдельно.

Стандартная библиотека C - <stdio.h>

Другие расширения