Почему std :: fstream устанавливает бит EOF так, как он это делает? - PullRequest
15 голосов
/ 24 июня 2009

Недавно я столкнулся с проблемой, вызванной использованием fstream :: eof (). Я прочитал следующую строку из здесь :

Функция eof () возвращает true, если достигнут конец связанного входного файла, в противном случае - false.

и (ошибочно) предположил, что это означало, что если бы я использовал fstream :: read () и прочитал после конца файла, функция eof () сказала бы мне. Поэтому я сделал что-то вроде этого (очень обобщенно):

for(int i = 0; i < max && !file.eof(); i++)
{
     file.read(mything, sizeof(mything));
}

Проблема возникла из-за того, что было объяснено позже на странице, указанной выше (которую я не смог прочитать изначально из-за вводящего в заблуждение первого абзаца):

И наоборот, поток не переходит в состояние EOF, если после последнего токена есть пробелы, но попытка прочитать другой токен все равно не удастся. Поэтому флаг EOF нельзя использовать в качестве теста в цикле, предназначенном для чтения всего содержимого потока до EOF. Вместо этого следует проверять состояние сбоя после попытки чтения.

Итак, я изменился, и теперь мой цикл проверяет файл file.fail (), а не file.eof (), и я понимаю, КАК eof () работает. Мой вопрос: почему это так работает? Есть ситуации, когда это желательно? Мне кажется, что после того, как вы прошли EOF, вы прошли EOF и eof () должна вернуть true.

UPDATE Спасибо за ответы, я думаю, что получил. Единственная операция, которую я выполняю, - это read (), и я немедленно проверяю fail (), поэтому я думаю, что все в порядке. Теперь мой вопрос: для чего будет , для которого я использую eof ()?

Ответы [ 2 ]

16 голосов
/ 24 июня 2009

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

Это отражает функциональность системного вызова read, который обычно завершается вызовом файлового ввода-вывода (win32 может вызвать ReadFile, но я считаю, что функциональность аналогична).

Из справочной страницы read «ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ» (выделение добавлено):

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

Кстати: хороший способ написать то, что вы хотели, будет выглядеть так:

T something;
while(file.read(something, sizeof(something))) {
    // process your 'something'
}

это работает, потому что file.read (как и многие члены iostream) возвращают ссылку на сам iostream. Все они имеют перегруженный оператор, чтобы позволить тестировать состояние потока. Аналогично чтению из std::cin, while(std::cin >> x) { ... } также работает.

РЕДАКТИРОВАТЬ: вы должны знать, что тестирование против сбоя может быть одинаково неправильно по той же причине. Со страницы, на которую вы ссылаетесь fail(), возвращается, если предыдущая операция завершилась неудачно. Это означает, что вам нужно выполнить чтение или другую соответствующую операцию перед тестированием.

0 голосов
/ 29 апреля 2012
int n;
std::cin >> n >> std::stripws;

исправляет эту проблему. в этот момент вы можете использовать .good () или .eof (). Мне нравится использовать .good (), так как при наличии плохого дискового блока .good () его обнаружит. но это я .eof () не будет, вам также придется добавить .fail () || .bad ().

Я только что узнал об этом после некоторого усердного изучения проблемы потребления пустого пространства. Я собирался предложить ECO для iostream и ifstream, и вот, это уже сделано. : -D

...