我刚刚在这个答案中发现了一条评论,说在循环条件中使用ioStream::EOF
“几乎肯定是错误的”。 我通常使用类似while(cin>>n)
-我猜它隐式地检查EOF。
为什么在(!cin.eof())错误的情况下使用显式检查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
1 2 a 3 4 5
a
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-loop终结者。
到目前为止还不错,但是如果流中有尾随空间会发生什么呢--这听起来像是反对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)
无疑是一种更常见,更简洁的习惯用法,在简单(每个读取类型的单个数据)的场景中可能是首选。
因为如果程序员不在(stream>>n)时编写,他们可能会编写如下内容:
while(!stream.eof())
{
stream >> n;
//some work on n;
}
这里的问题是,在不首先检查流读取是否成功的情况下,您不能在N上执行某些工作,因为如果不成功,您在N
上执行的某些工作将产生不希望的结果。
整个要点是,EOFBIT
,BADBIT
或FAILBIT
是在尝试从流中读取之后设置的。 因此,如果stream>> n
失败,则立即设置eOfbit
,badbit
或failbit
,因此如果您编写while(stream>>n)
,则更常用,因为如果从流中读取失败,返回的对象stream
将转换为false
,因此循环停止。 如果读取成功并且循环继续,则转换为true
。