Я только что нашел комментарий в этом ответе о том, что использование iostream::eof
в условии цикла «почти наверняка неправильно». Я обычно использую что-то вроде while(cin>>n)
- что, я думаю, неявно проверяет EOF.
Почему проверка на eof явно использует while (!cin.eof())
неправильно?
Чем он отличается от использования scanf("...",...)!=EOF
в C (который я часто использую без проблем)?
scanf(...) != EOF
не будет работать в C, потому чтоscanf
возвращает количество полей, успешно проанализированных и назначенных. Правильное условие ,scanf(...) < n
гдеn
это число полей в строке формата.EOF
если конец файла встречается до первого преобразования поля (успешно или нет). Если между полями достигнут конец файла, он вернет количество полей, успешно преобразованных и сохраненных. Что делает сравнениеEOF
неправильно..eof()
после выхода из цикла.while(fail)
цикл завершается как фактическим отказом, так и результатом. Подумайте, требуется ли вам 3 дюйма на итерацию (скажем, вы читаете точку xyz или что-то в этом роде), но в потоке ошибочно только два целых числа.Ответы:
Потому что
iostream::eof
вернется толькоtrue
после прочтения конца потока. Это не означает, что следующее чтение будет концом потока.Рассмотрим это (и предположим, что следующее чтение будет в конце потока):
Против этого:
И на ваш второй вопрос: потому что
такой же как
и не такой как
источник
int
s илиstd::string
S или подобное, то бит EOF будет установлен , когда вы извлекаете одно право до конца и добыча попадает в конец. Вам не нужно читать снова. Причина, по которой он не устанавливается при чтении из файлов, заключается в том, что\n
в конце есть дополнительное . Я рассказал об этом в другом ответе . Чтениеchar
s - это другое дело, потому что оно извлекает только по одному за раз и не доходит до конца.while (!eof())
то, что не будет «работать» наint
s илиstd::string
s, когда входные данные полностью пусты, поэтому даже зная, что нет конечной\n
заботы, не требуется."Hello"
(без пробелов или\n
) и astd::string
извлечен, он извлечет буквы изH
вo
, прекратит извлекать и тогда не устанавливайте бит EOF. Фактически, он установит бит EOF, потому что это был EOF, который остановил извлечение. Просто в надежде прояснить это для людей.Итог: при правильной обработке пробелов, вот как
eof
можно использовать (и даже быть более надежным, чемfail()
для проверки ошибок):( Спасибо Тони Д. за предложение выделить ответ. См. Его комментарий ниже для примера, почему это более надежно. )
Главный аргумент против использования,
eof()
кажется, пропускает важную тонкость о роли пустого пространства. Мое предложение состоит в том, что проверкаeof()
явно не только не « всегда неправильна » - что, по-видимому, является основным мнением в этом и аналогичных потоках SO, - но при правильной обработке пустого пространства она обеспечивает более чистую и надежную работу. обработка ошибок, и это всегда правильное решение (хотя, не обязательно самое кратчайшее).Подводя итог тому, что предлагается в качестве «правильного» завершения и порядка чтения, можно сделать следующее:
Сбой из-за попытки чтения после 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-й).Принимая во внимание следующее:
Здесь
in.fail()
проверяется, что до тех пор, пока есть, что читать, оно является правильным. Это не просто терминатор цикла while.Пока все хорошо, но что произойдет, если в потоке есть конечный пробел - что звучит как главная проблема, связанная с
eof()
терминатором?Нам не нужно отказываться от обработки ошибок; просто съешь пробел:
std::ws
пропускает любой потенциальный (ноль или более) завершающий пробел в потоке при установкеeofbit
, а неfailbit
. Таким образом,in.fail()
работает, как ожидалось, если есть хотя бы одна информация для чтения. Если все пустые потоки также приемлемы, тогда правильная форма:Резюме. Правильно построенная структура
while(!eof)
не только возможна и не ошибочна, но и позволяет локализовать данные в рамках области и обеспечивает более четкое отделение проверки ошибок от обычного бизнеса. Это , как говорится,while(!fail)
это бесспорно более распространенным и лаконична идиомы, и может быть предпочтительным в простой (единичные данные на чтение типа) сценариев.источник
eofbit
иfailbit
установлены, в другом толькоfailbit
установлены. Вам нужно проверить это только один раз после завершения цикла, а не на каждой итерации; он выйдет из цикла только один раз, поэтому вам нужно только проверить, почему он вышел из цикла один раз.while (in >> data)
прекрасно работает для всех пустых потоков.!eof & fail
прошедший цикл. Есть случаи, когда на это нельзя положиться. Смотрите выше комментарий ( goo.gl/9mXYX ). В любом случае, я не предлагаю -eof
проверять как всегда лучшую альтернативу. Я просто говорю, что это возможное и (в некоторых случаях более подходящий) способ сделать это, а не «безусловно , не так!» как это обычно утверждается здесь, в SO.stream >> my_int
котором поток содержит , например , «-»:eofbit
иfailbit
являются набор. Это хужеoperator>>
сценария, когда предоставленная пользователем перегрузка, по крайней мере, имеет возможность очистки,eofbit
прежде чем вернуться для поддержкиwhile (s >> x)
использования. В более общем смысле, в этом ответе может быть использована очистка - только финал,while( !(in>>ws).eof() )
как правило, является надежным, и он похоронен в конце.Потому что, если программисты не пишут
while(stream >> n)
, они могут написать это:Здесь проблема в том, что вы не можете обойтись
some work on n
без предварительной проверки, было ли чтение потока успешным, потому что, если бы оно было неудачным, выsome work on n
бы получили нежелательный результат.Все дело в том , что
eofbit
,badbit
илиfailbit
устанавливаются после того, как попытка чтения из потока. Таким образом, если происходитstream >> n
сбой, тоeofbit
,badbit
илиfailbit
устанавливается сразу, так что это более идиоматично, если вы пишетеwhile (stream >> n)
, потому что возвращаемый объектstream
конвертируется в,false
если при чтении из потока произошел сбой, и, следовательно, цикл останавливается. И он конвертируется в,true
если чтение было успешным, и цикл продолжается.источник
n
, программа может также попасть в бесконечный цикл , если сбойная операция потока не потребляет никакого ввода.Другие ответы объяснили, почему логика ошибочна
while (!stream.eof())
и как ее исправить. Я хочу сосредоточиться на чем-то другом:В общих чертах, проверка
eof
только для неправильна, потому что поток извлечения (>>
) может завершиться неудачно, не попав в конец файла. Если у вас есть, например,int n; cin >> n;
и поток содержитhello
, тоh
не является действительной цифрой, поэтому извлечение не удастся, не достигнув конца ввода.Эта проблема в сочетании с общей логической ошибкой проверки состояния потока перед попыткой чтения из него, что означает, что для N входных элементов цикл будет выполняться N + 1 раз, приводит к следующим симптомам:
Если поток пуст, цикл будет запущен один раз.
>>
потерпит неудачу (нет входных данных для чтения), и все переменные, которые должны были быть установлены (bystream >> x
), фактически неинициализированы. Это приводит к тому, что данные мусора обрабатываются, что может привести к бессмысленным результатам (часто огромным количествам).(Если ваша стандартная библиотека соответствует C ++ 11, то теперь все немного по-другому: сбой
>>
теперь устанавливает числовые переменные0
вместо того, чтобы оставлять их неинициализированными (кромеchar
s).)Если поток не пустой, цикл будет запущен снова после последнего допустимого ввода. Поскольку в последней итерации все
>>
операции не выполняются, переменные, скорее всего, сохранят свое значение по сравнению с предыдущей итерацией. Это может проявляться как «последняя строка печатается дважды» или «последняя входная запись обрабатывается дважды».(Это должно проявляться немного иначе, чем в C ++ 11 (см. Выше): теперь вы получаете «фантомную запись» с нулями вместо повторяющейся последней строки.)
Если поток содержит искаженные данные, но вы только проверяете
.eof
, вы получаете бесконечный цикл.>>
не удастся извлечь какие-либо данные из потока, поэтому цикл вращается, даже не достигнув конца.Напомним: Решение испытать успех
>>
самой операции, а не использовать отдельный.eof()
метод:while (stream >> n >> m) { ... }
так же , как в C вы проверить успешностьscanf
самого вызова:while (scanf("%d%d", &n, &m) == 2) { ... }
.источник