Тестирование stream.good() или !stream.eof() считывает последнюю строку дважды

Возможный дубликат:
Почему считается ли iostream::eof внутри условия цикла неправильным?

У меня есть следующий фрагмент кода:

ifstream f("x.txt");
string line;
while (f.good()) {
  getline(f, line);
  // Use line here.
}

Но это читает последнюю строку дважды. Почему это происходит и как это исправить?

Что-то очень похожее происходит с:

ifstream f("x.txt");
string line;
while (!f.eof()) {
  getline(f, line);
  // Use line here.
}

person Prabhu    schedule 01.12.2010    source источник
comment
Как это дубликат? В другом ответе даже не упоминается цикл с функцией good() в качестве теста.   -  person Jerry Jeremiah    schedule 16.05.2014
comment
@JerryJeremiah Это та же общая проблема, связанная с попыткой проверить текущее состояние потока (например, .good(), .eof(), .fail()), чтобы предсказать успех будущих операций чтения (например, getline, >>). Это не работает, потому что состояние потока сообщает вам, была ли предыдущая операция чтения неудачной или достигла конца ввода; это ничего не говорит вам о будущих попытках чтения.   -  person melpomene    schedule 04.05.2019


Ответы (3)


Вы очень, очень редко хотите проверять плохие, плохие и хорошие. В частности, для eof (поскольку !stream.eof() является распространенной ошибкой), поток, находящийся в настоящее время в EOF, не обязательно означает, что последняя операция ввода не удалась; и наоборот, отсутствие EOF не означает, что последний ввод был успешным.

Все функции состояния потока — fail, bad, eof и good — сообщают вам о текущем состоянии потока, а не предсказывают успех будущей операции. Проверьте сам поток (что эквивалентно инвертированной проверке на отказ) после нужной операции:

if (getline(stream, line)) {
  use(line);
}
else {
  handle_error();
}

if (stream >> foo >> bar) {
  use(foo, bar);
}
else {
  handle_error();
}

if (!(stream >> foo)) {  // operator! is overloaded for streams
  throw SomeException();
}
use(foo);

Чтобы прочитать и обработать все строки:

for (std::string line; getline(stream, line);) {
  process(line);
}

Примечательно, что good() назван неправильно и не эквивалентен тестированию самого потока (что и делают приведенные выше примеры).

person Fred Nurk    schedule 01.12.2010
comment
Часть о проверке eof верна, но предложение проверить сам поток немного не так. good() означает, что ни один из eofbit, badbit или failbit не установлен. fail() означает, что установлен либо badbit, либо failbit. Проверка потока (либо с помощью преобразования void*, либо с помощью оператора !) точно такая же, как и вызов функции-члена fail(). - person KeithB; 01.12.2010
comment
@KeithB: Вы могли заметить, что я исключил ошибку из группы, которую следует проверять редко. Неудачный поток — вот что важно, и проверка самого потока почти всегда более удобна, чем эквивалентная функция fail(). Сравните getline(поток, строка) с !getline(поток, строка).fail(). - person Fred Nurk; 01.12.2010

Просто используйте

ifstream f("x.txt");
while (getline(f, line)) {
    // whatever
}

Это идиоматический способ написать такой цикл. Я не смог воспроизвести ошибку (на машине с Linux).

person Fred Foo    schedule 01.12.2010
comment
Я только что понял, почему это работает: последний успешный вызов getline() может установить eof, если в конце последней строки нет новой строки. Бит fail устанавливается только при неудачном вызове getline(). Таким образом, мы не хотим заканчивать цикл на eof, но хотим заканчивать его на fail. - person Aaron McDaid; 20.05.2011
comment
Еще одна вещь.. Я делал while(f.peek() != EOF) { ... }. Я думаю, это правильно? Но я воспользуюсь вашим ответом в будущем. - person Aaron McDaid; 20.05.2011

Он не читал последнюю строку дважды, но поскольку ему не удалось прочитать, когда он достиг eof, ваша строковая строка имеет значение, которое она имела ранее.

Это потому, что f больше не является «хорошим», когда он прочитал EOF, а не когда он собирается его прочитать.

person CashCow    schedule 01.12.2010