为什么iostream :: eof里面的循环条件被认为是错误的?

我刚刚在这个答案中发现了一个评论,说在循环条件下使用iostream::eof是“几乎肯定是错误的”。 我通常使用类似while(cin>>n) – 我猜隐式检查EOF,为什么显式检查使用iostream::eof错误的iostream::eof

这与在C中使用scanf("...",...)!=EOF (我经常使用没有问题)有什么不同?

因为iostream::eof只会读取stream结束后才返回true 。 它并不表示,下一次读取将是stream的结束。

考虑这一点(并假设接下来的阅读将在stream的末尾):

 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的build议,突出了答案。下面看他的评论,为什么这个更强大。


反对使用eof()的主要观点似乎缺less了关于空白作用的一个重要的微妙之处。 我的提议是,明确地检查eof()不仅不是“ 总是错误的 ” – 这似乎是在这个和类似的SO线程中的压倒一切的意见 – ,但正确处理空白,它提供了一个清洁和更可靠的error handling,并始终是正确的解决scheme(虽然不一定是最好的)。

总结什么是build议作为“正确的”终止和阅读顺序如下:

 int data; while(in >> data) { /* ... */ } // which is equivalent to while( !(in >> data).fail() ) { /* ... */ } 

由于读取尝试超出eof的故障被视为终止条件。 这意味着没有简单的方法来区分一个成功的stream和一个真正由于eof以外的原因而失败的stream。 采取以下stream程:

  • 1 2 3 4 5<eof>
  • 1 2 a 3 4 5<eof>
  • a<eof>

while(in>>data)所有三个input的一组failbit终止。 在第一和第三, eofbit也设置。 所以过去的循环需要非常难看的额外的逻辑来区分正确的input(第一)和不正确的input(第二和第三)。

鉴于以下几点:

 while( !in.eof() ) { int data; in >> data; if ( in.fail() ) /* handle with break or throw */; // now use data } 

在这里, in.fail()validation只要有东西要读,就是正确的。 它的目的不仅仅是while循环终结者。

到目前为止这么好,但是如果stream中有尾随的空间会发生什么 – 听起来像eof()作为终止符的主要担心呢?

我们不需要放弃我们的error handling; 只是吃了空白:

 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同时跳过stream中任何可能的(零个或多个)尾随空间。 因此,只要有至less一个数据要读取, in.fail()按预期工作。 如果全空白stream也可以接受,那么正确的forms是:

 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)是无可争议的一个更常见和简洁的习惯用法,并可能是首选的简单(单一数据每读取types)的情况。

因为如果程序员不写while(stream >> n) ,他们可能会这样写:

 while(!stream.eof()) { stream >> n; //some work on n; } 

这里的问题是,如果没有首先检查stream读取是否成功,你不能some work on nsome work on n ,因为如果不成功,你some work on nsome work on n会产生不希望的结果。

整个观点是, 在尝试从stream中读取之后eofbitbadbitfailbit被设置 所以如果stream >> n失败了,那么eofbitbadbit或者failbit会立即被设置,所以如果你写while (stream >> n) ,那么它更习惯,因为如果读取失败,返回的对象stream将转换为falsestream,因此循环停止。 如果读取成功并且循环继续,则它转换为true

 1 while (!read.fail()) { 2 cout << ch; 3 read.get(ch); 4 } 

如果您使用第3行中的第2行和第2行中的第3行,您将获得两次ch打印。 所以在阅读之前。