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

Я только что нашел комментарий в этот ответ, сказав, что с помощью iostream::eof в состоянии цикла «почти наверняка неправильно». Я вообще использую что-то вроде while(cin>>n) — который я предполагаю, что неявно проверяет EOF, почему проверяет eof явно с помощью iostream::eof неправильно?

Чем он отличается от использования scanf("...",...)!=EOF в С (который я часто использую без проблем)?

516

Решение

Так как iostream::eof вернется только true после читая конец потока. Оно делает не указать, что следующее чтение будет концом потока.

Рассмотрим это (и предположим, что следующее чтение будет в конце потока):

while(!inStream.eof()){
int data;
// yay, not end of stream yet, now read ...
inStream >> data;
// oh crap, now we read the end and *only* now the eof bit will be set (as well as the fail bit)
// do stuff with (now uninitialized) data
}

Против этого:

int data;
while(inStream >> data){
// when we land here, we can be sure that the read was successful.
// if it wasn't, the returned stream from operator>> would be converted to false
// and the loop wouldn't even be entered
// do stuff with correctly initialized data (hopefully)
}

И на ваш второй вопрос: потому что

if(scanf("...",...)!=EOF)

такой же как

if(!(inStream >> data).eof())

а также не такой же как

if(!inStream.eof())
inFile >> data
468

Другие решения

Итог сверху: При правильной обработке пустого пространства, вот как eof может быть использован (и даже, быть более надежным, чем fail() для проверки ошибок):

while( !(in>>std::ws).eof() ) {
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}

(Спасибо Тони Д. за предложение выделить ответ. Посмотрите его комментарий ниже для примера того, почему это более надежно.)


Основной аргумент против использования eof() кажется, отсутствует важная тонкость в отношении роли пустого пространства. Мое предложение таково, что проверка eof() явно не только нетвсегда неправильно«- что, по-видимому, является основным мнением в этом и аналогичных потоках SO, — но при правильной обработке пробелов это обеспечивает более чистую и надежную обработку ошибок и является всегда правильно решение (хотя, не обязательно, кратчайшее).

Подводя итог тому, что предлагается в качестве «правильного» завершения и порядка чтения, можно сделать следующее:

int data;
while(in >> data) {  /* ... */ }

// which is equivalent to
while( !(in >> data).fail() )  {  /* ... */ }

Сбой из-за попытки чтения после eof принимается как условие завершения. Это означает, что нет простого способа отличить успешный поток от потока, который действительно не работает по причинам, отличным от eof. Возьмите следующие потоки:

  • 1 2 3 4 5<eof>
  • 1 2 a 3 4 5<eof>
  • a<eof>

while(in>>data) заканчивается набором failbit за все три входа. В первом и третьем eofbit также установлен. Таким образом, после цикла нужна очень уродливая дополнительная логика, чтобы отличить правильный ввод (1-й) от неправильного (2-й и 3-й).

Принимая во внимание следующее:

while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}

Вот, in.fail() проверяет, что до тех пор, пока есть, что читать, оно является правильным. Это не просто терминатор цикла while.

Пока все хорошо, но что произойдет, если в потоке будет отставание — что звучит как основная проблема против eof() как терминатор?

Нам не нужно отказываться от обработки ошибок; просто съешь пробел:

while( !in.eof() )
{
int data;
in >> data >> ws; // eat whitespace with std::ws
if ( in.fail() ) /* handle with break or throw */;
// now use data
}

std::ws пропускает любое потенциальное (ноль или более) конечное пространство в потоке при установке eofbit, а также не failbit. Так, in.fail() работает как положено, если есть хотя бы одна информация для чтения. Если все пустые потоки также приемлемы, тогда правильная форма:

while( !(in>>ws).eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
/* this will never fire if the eof is reached cleanly */
// now use data
}

Резюме: Правильно построенный while(!eof) это не только возможно и не неправильно, но позволяет локализовать данные в пределах области и обеспечивает более четкое отделение проверки ошибок от бизнеса, как обычно. Что, как говорится, while(!fail) это бесспорна более распространенная и лаконична идиома, и может быть предпочтительным в простых (одиночных данных для каждого типа считывания) сценарии.

94

Потому что, если программисты не пишут while(stream >> n)они могут написать это:

while(!stream.eof())
{
stream >> n;
//some work on n;
}

Здесь проблема в том, что вы не можете сделать some work on n без предварительной проверки, если чтение потока было успешным, потому что, если это было неудачно, ваш some work on n приведет к нежелательному результату.

Все дело в том, что eofbit, badbit, или же failbit установлены после попытки чтения из потока. Так что если stream >> n не получается, тогда eofbit, badbit, или же failbit устанавливается сразу, поэтому его более идиоматично, если вы пишете while (stream >> n)потому что возвращаемый объект stream превращается в false если произошел сбой при чтении из потока и, следовательно, цикл останавливается. И это превращается в true если чтение прошло успешно и цикл продолжается.

63

1 while (!read.fail()) {
2     cout << ch;
3     read.get(ch);
4 }

Если вы используете строку 2 в 3 и строку 3 в 2, вы получите ch напечатано дважды.
Так что cout перед прочтением.

-4
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector