我刚刚在这个答案表示,在循环条件下使用iostream::eof
"几乎肯定是错误的"。我通常使用while(cin>>n)
之类的东西-我猜它会隐式检查EOF。
为什么要使用while(!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之外,没有简单的方法来区分成功的流和确实失败的流。采取以下流:
12345
12a345
a
while(in>>data)
以 all 三个输入的设置的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
<时,跳过流中任何潜在的(零个或多个)尾随空间. code>
时,跳过流中任何潜在的(零个或多个)尾随空间.>
。因此,只要有至少一个数据要读取,
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;
}
这里的问题是,如果不首先检查流读取是否成功,就不能对n执行某些代码,因为如果不成功,则您对n进行某些代码就不能工作。会产生不希望的结果。
整个问题是,在尝试从计算机中读取eofbit
,badbit
或failbit
时,设置为。 因此,如果stream>>n
失败,则eofbit
,badbit
或failbit
立即设置,因此如果您编写while(stream>>n)
,则设置更为惯用,因为返回的对象stream
如果存在,将转换为false
从流中读取失败,因此循环停止。如果读取成功并且循环继续,它将转换为true
。
其他答案已解释了为什么while(!stream.eof())
中的逻辑错误以及如何解决该问题。我想专注于其他事情:
为什么使用
iostream::eof
明确检查eof是错误的?
一般而言,仅检查eof
是错误的,因为流提取(>>
)可能会失败而不会到达文件末尾。如果您有intn;cin >> n;
,并且流包含hello
,则h
不是有效数字,因此提取将失败而未到达输入的结尾。 / p>
此问题,再加上在尝试从流中读取之前检查流状态的一般逻辑错误,这意味着对于N个输入项,循环将运行N + 1次,导致以下情况症状:
如果流为空,则循环将运行一次。 >>
将失败(没有要读取的输入),并且所有应设置的变量(通过stream>>x
)实际上都未初始化。这导致对垃圾数据进行处理,这可能表现为无意义的结果(通常是巨大的数字)。
(如果您的标准库符合C ++ 11,现在情况会有所不同:失败的>>
现在将数字变量设置为0
,而不是不进行初始化(char
s除外)。
如果流不为空,则循环将在最后一个有效输入之后再次运行。由于在最后一次迭代中所有>>
操作均失败,因此变量很可能会保留上一次迭代中的值。这可以表现为"最后一行被打印两次"或"最后一条输入记录被处理两次"。
(这与C ++ 11以来的表现有所不同(见上文):现在您得到了零的"幻像记录",而不是重复的最后一行。)
如果流中包含格式错误的数据,但仅检查.eof
,则会导致无限循环。 >>
将无法从流中提取任何数据,因此循环旋转到位而不到达终点。
回顾:解决方案是测试>>
操作本身是否成功,而不是使用单独的.eof()
方法:while(stream >> n >> m){...}
,就像在C中一样,您测试scanf
调用自身是否成功:while(scanf("%d%d",&n,&m)== 2){...}
。