为什么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 nsome work on n ,因为如果它不成功,你some work on nsome work on n会产生不希望的结果。

    整个观点是, 在尝试从流中读取数据后eofbitbadbitfailbit被设置 因此,如果stream >> n失败,那么立即设置eofbitbadbitfailbit ,所以如果您在写入while (stream >> n) ,它更具惯用性,因为如果读取失败,返回的对象stream将转换为false流,因此循环停止。 如果读取成功并且循环继续,它将转换为true

    链接地址: http://www.djcxy.com/p/29803.html

    上一篇: Why is iostream::eof inside a loop condition considered wrong?

    下一篇: Difference between declaring variables before or in loop?