为什么iostream :: eof在循环中被认为是错误的?
我刚刚在这个回答中发现了一条评论,说在循环条件下使用iostream::eof
“几乎肯定是错误的”。 我通常使用像while(cin>>n)
- 我猜隐式检查EOF,为什么显式检查使用iostream::eof
错误的iostream::eof
?
它与C中使用scanf("...",...)!=EOF
(我经常使用没有问题)有什么不同?
因为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线程中的压倒一切的观点 - ,但通过适当处理空白空间,它提供了一个更清洁和更可靠的错误处理,并始终是正确的解决方案(尽管不一定是最好的)。
总结什么是建议作为“正确的”终止和阅读顺序如下:
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
也设置。 所以过去的循环需要非常难看的额外逻辑来区分正确的输入(第一)和不正确的输入(第二和第三)。
鉴于以下情况:
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)
无疑是一种更常见和更简洁的习惯用法,并且可能在简单(每种读取类型的单个数据)情况下更受欢迎。
因为如果程序员不写while(stream >> n)
,他们可能会这样写:
while(!stream.eof())
{
stream >> n;
//some work on n;
}
这里的问题是,如果没有首先检查流读取是否成功,你不能some work on 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
。
上一篇: Why is iostream::eof inside a loop condition considered wrong?
下一篇: Difference between declaring variables before or in loop?