Почему :: feof () отличается от :: _ eof (:: fileno ())? - PullRequest
0 голосов
/ 09 января 2012

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Не используйте ::feof() в качестве условия цикла. Например, см. Ответ на: чтение файла: feof () для двоичных файлов

Однако у меня есть «реальный» код, который демонстрирует проблему, в которой не используется ::feof() в качестве условия моего цикла, но ЛОГИЧЕСКИ, это самый простой способ продемонстрировать проблему.

Рассмотрим следующее: Мыитерация потока символов по одному:

FILE* my_file;
// ...open "my_file" for reading...

int c;
while(0 == ::feof(my_file))
{ // We are not at EOF
  c = ::getc(my_file);
  // ...process "c"
}

Приведенный выше код работает, как и ожидалось: Файл обрабатывается по одному символу за разEOF, мы выпадаем.

ОДНАКО, следующее имеет неожиданное поведение :

FILE* my_file;
// ...open "my_file" for reading...

int c;
while(0 == ::_eof(::fileno(my_file)))
{ // We are not at EOF
  c = ::getc(my_file);
  // ...process "c"
}

Я бы ожидал, что они будут выполнять то же самое.::fileno() правильно возвращает (целочисленный) дескриптор файла каждый раз.Однако тест ::_eof(::fileno(my_file)) работает ровно один раз , а затем возвращает 1 (указывая EOF) со второй попытки.

Я не понимаю этого.

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

Что происходит?

(Файлы открываются как «текст», это текстовые файлы ASCII с дюжиной строк, MSVS2008, Win7 / 64.)

1 Ответ

5 голосов
/ 09 января 2012

Полагаю, вполне возможно, что :: feof () "буферизован" (поэтому он работает правильно) while :: _ eof () "небуферизован" и считает весь файл уже "читается" (потому что весь файл мог бы вписаться в первый блок считывается с диска). Однако это не может быть правдой учитывая назначение этих функций. Итак, я действительно в растерянности.

Я не знаю, почему вы думаете, что это "не может быть правдой, учитывая назначение этих функций". Эти 2 функции предназначены для работы с файлами, которые открываются и обрабатываются по-разному, поэтому они несовместимы.

На самом деле, именно это и происходит. Попробуйте это:

FILE* my_file;
// ...open "my_file" for reading...

int c;
while(0 == ::_eof(::fileno(my_file)))
{ // We are not at EOF
  c = ::getc(my_file);

  long offset1 = ftell(my_file);
  long offset2 = _tell(fileno(my_file));

  if (offset1 != offset2)
  {
     //here you will see that the file pointers are different
     //which means that _eof and feof will fire true under different conditions
  }
  // ...process "c"
}

Я постараюсь уточнить немного на основе вашего комментария.

Когда вы вызываете fopen, вы получаете указатель на файл stream . Базовый объект потока хранит свой собственный указатель файла, который отделен от фактического указателя файла, связанного с базовым файловым дескриптором.

Когда вы звоните _eof, вы спрашиваете, достигли ли вы конца текущего файла. Когда вы вызываете feof, вы спрашиваете, достигли ли вы конца файла stream . Поскольку потоки файлов обычно буферизуются, конец файла достигается до конца потока.

Я все еще пытаюсь понять ваш ответ ниже, и какова цель для _eof (), если он всегда возвращает 1, даже если вы не читали что угодно (после первого символа).

Чтобы ответить на этот вопрос, цель _eof состоит в том, чтобы определить, достигли ли вы конца файла при использовании _open и _read для работы непосредственно с файловыми дескрипторами, а не когда вы используете fopen и fread или getc для работы с файловыми потоками. .

...