提问者:小点点

为什么IOStream::EOF在循环条件中(即`while(!stream.eof())`)被认为是错误的?


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

为什么在(!cin.eof())错误的情况下使用显式检查eof?

它与在C语言中使用scanf(“。。。”,。。。)!=EOF(我经常使用它,没有任何问题)有什么不同?


共3个答案

匿名用户

因为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上执行的某些工作将产生不希望的结果。

整个要点是,EOFBITBADBITFAILBIT是在尝试从流中读取之后设置的。 因此,如果stream>> n失败,则立即设置eOfbitbadbitfailbit,因此如果您编写while(stream>>n),则更常用,因为如果从流中读取失败,返回的对象stream将转换为false,因此循环停止。 如果读取成功并且循环继续,则转换为true