Тестването на 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 и добро. По-специално за 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 извън групата, която трябва да се проверява рядко. Неуспешният поток е това, което е важно и проверката на самия поток е почти винаги по-удобна от еквивалентния fail(). Сравнете getline(stream, line) с !getline(stream, line).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