Почему std:: fstream устанавливает бит EOF так, как он делает?

Недавно я столкнулся с проблемой, вызванной использованием 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(), а не файл .eof(), и я понимаю, что работает HOW(). Мой вопрос: почему он работает именно так? Существуют ли ситуации, когда это желательно? Мне кажется, что как только вы передали EOF, вы передали EOF, а eof() должен вернуть true.

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

Ответы

Ответ 1

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

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

Из раздела read manpage "RETURN VALUE" (выделено мной):

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

Кстати: хороший способ написать то, что вы хотели, будет следующим:

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

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

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

Ответ 2

int n;
std::cin >> n >> std::stripws;

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

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