Я только что нашел комментарий в этом, отвечая тем, что использование iostream::eof
в условиях цикла является "почти наверняка неправильным". Обычно я использую что-то вроде while(cin>>n)
, которое, как я предполагаю, неявно проверяет EOF, почему проверка того, что eof явно использует iostream::eof
неправильно?
Как это отличается от использования scanf("...",...)!=EOF
в C (который я часто использую без проблем)?
Потому что 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
Верх нижней части: При правильной обработке белого пространства, как eof
можно использовать (и даже быть более надежным, чем fail()
для проверки ошибок):
while( !(in>>std::ws).eof() ) {
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
(Спасибо Tony D за предложение выделить ответ. См. его комментарий ниже для примера, почему это более надежное.)
Основной аргумент против использования eof()
, похоже, не содержит важной тонкости в отношении роли белого пространства. Мое предложение состоит в том, что проверка eof()
явно не только не всегда "всегда ошибочна", что, по-видимому, является основным мнением в этом и подобном потоке SO, но при правильном обращении с белым пространством он обеспечивает чище и более надежная обработка ошибок, и это всегда правильное решение (хотя и не обязательно tersest).
Подводя итог тому, что предлагается, поскольку "правильное" окончание и порядок чтения следующие:
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()
проверяет, что до тех пор, пока есть что прочитать, он правильный. Цель - не просто терминатор цикла.
До сих пор так хорошо, но что происходит, если в потоке есть конечное пространство - что звучит как основная проблема с 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)
является, несомненно, более распространенной и краткой идиомой, и может быть предпочтительнее в простых (одиночных данных для типа чтения).
eofbit
и failbit
, в другом - только failbit
. Вам нужно проверить это только один раз после завершения цикла, а не на каждой итерации; он выйдет из цикла только один раз, поэтому вам нужно только проверить, почему он вышел из цикла один раз. while (in >> data)
отлично работает для всех пустых потоков.
!eof & fail
past loop. Есть случаи, в которых нельзя на это положиться. Смотрите выше комментарий ( goo.gl/9mXYX ). В любом случае, я не предлагаю eof
-check как всегда лучшую альтернативу. Я просто говорю, что это возможно , и (в некоторых случаях более подходящим) способ сделать это, а не «безусловно , не так!» как это обычно утверждается здесь, в SO.
Потому что, если программисты не пишут 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
, если чтение было успешным, и цикл продолжается.
n
, программа может также попасть в бесконечный цикл , если сбойная операция потока не потребляет никакого ввода.
1 while (!read.fail()) {
2 cout << ch;
3 read.get(ch);
4 }
Если вы используете строку 2 в 3 и строку 3 в 2, вы дважды печатаете ch
.
Итак, cout перед чтением.
ch
на первой итерации?
scanf(...) != EOF
также не будет работать в C, потому чтоscanf
возвращает количество полей, успешно проанализированных и назначенных. Правильное условие:scanf(...) < n
гдеn
- количество полей в строке формата.