为什么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 n
做some work on n
,因为如果不成功,你some work on n
的some work on n
会产生不希望的结果。
整个观点是, 在尝试从stream中读取之后 , eofbit
, badbit
或failbit
被设置。 所以如果stream >> n
失败了,那么eofbit
, badbit
或者failbit
会立即被设置,所以如果你写while (stream >> n)
,那么它更习惯,因为如果读取失败,返回的对象stream
将转换为false
stream,因此循环停止。 如果读取成功并且循环继续,则它转换为true
。
1 while (!read.fail()) { 2 cout << ch; 3 read.get(ch); 4 }
如果您使用第3行中的第2行和第2行中的第3行,您将获得两次ch
打印。 所以在阅读之前。