C++ 为什么循环条件(即,`while(!stream.eof())`)中的iostream::eof被认为是错误的?

C++ 为什么循环条件(即,`while(!stream.eof())`)中的iostream::eof被认为是错误的?,c++,iostream,c++-faq,C++,Iostream,C++ Faq,我刚刚在回答中发现一条评论说,在循环条件中使用iostream::eof“几乎肯定是错误的”。我通常使用while(cin>>n)-我猜这会隐式地检查EOF 为什么显式使用检查eof而(!cin.eof())错误 它与使用scanf(“…”,…)有何不同=EOF在C中(我经常使用它,没有问题)?因为iostream::EOF只会在读取流的结尾后返回true。它并不表示下一次读取将是流的结束 考虑这一点(并假设下一次读取将在流的末尾): 针对这一点: int data; while(inStrea

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

为什么显式使用
检查eof而(!cin.eof())
错误


它与使用
scanf(“…”,…)有何不同=EOF
在C中(我经常使用它,没有问题)?

因为
iostream::EOF
只会在读取流的结尾后返回
true
。它并不表示下一次读取将是流的结束

考虑这一点(并假设下一次读取将在流的末尾):

针对这一点:

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
与不相同

if(!(inStream >> data).eof())
if(!inStream.eof())
    inFile >> data

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

while(!stream.eof())
{
    stream >> n;
    //some work on n;
}
这里的问题是,如果不首先检查流读取是否成功,您就不能对n
执行
某些工作,因为如果读取不成功,您的
对n
执行的某些工作将产生不希望的结果


整个要点是,
eofbit
badbit
failbit
在尝试从流中读取后设置为因此,如果
stream>>n
失败,那么
eofbit
badbit
failbit
会立即设置,因此,如果您在编写
时编写
,它会更惯用(流>>n)
,因为如果从流中读取失败,则返回的对象
将转换为
,因此循环停止。如果读取成功且循环继续,则返回的对象将转换为

底线顶部:正确处理空白,下面的wing是如何使用
eof
(甚至比
fail()
更可靠地进行错误检查):

(感谢Tony D提出的强调答案的建议。请参见下面他的评论,以获取一个示例,说明为什么这样做更为有力。)


反对使用
eof()
的主要论点似乎缺少了关于空格作用的一个重要细节。我的主张是,显式检查
eof()
不仅不是“总是错误的”--在本线程和类似的SO线程中,这似乎是一个压倒一切的观点-,但通过正确处理空白,它提供了更干净、更可靠的错误处理,并且始终是正确的解决方案(尽管不一定是最简洁的)

以下是建议的“适当”终止和读取顺序的总结:

int data;
while(in >> data) {  /* ... */ }

// which is equivalent to 
while( !(in >> data).fail() )  {  /* ... */ }
由于超出eof的读取尝试而导致的失败被视为终止条件。这意味着没有简单的方法来区分成功的流和由于eof以外的原因而真正失败的流。采用以下流:

  • 12345
  • 12345
  • a
while(在>>数据中)
终止时,所有三个输入都设置了一个
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)
无疑是一个更常见、更简洁的习惯用法,在简单(每读取类型一个数据)的场景中可能更受欢迎。

其他答案解释了(!stream.eof())
中逻辑错误的原因以及如何修复它。我想重点介绍一些不同的内容:

为什么显式使用
iostream::eof
检查eof是错误的

一般来说,只检查
eof
是错误的,因为流提取(
>
)可以在不到达文件结尾的情况下失败。如果您有例如
int n;cin>>n;
且流包含
hello
,则
h
不是有效数字,因此提取将在不到达输入结尾的情况下失败

此问题,再加上在尝试读取流状态之前检查流状态的一般逻辑错误,这意味着对于N个输入项,循环将运行N+1次,导致以下症状:

  • 如果流为空,循环将运行一次。
    将失败(没有要读取的输入),并且所有本应设置的变量(通过
    流>>x
    )实际上都未初始化。这将导致垃圾数据被处理,这可能表现为无意义的结果(通常是巨大的数字)

    (如果您的标准库符合C++11,那么现在情况就有点不同了:一个失败的
    >
    现在将数值变量设置为
    0
    ,而不是保持它们未初始化(除了
    char
    s)#include<iostream> #include<fstream> using namespace std; int main() { ifstream myFile("myfile.txt"); string x; while(!myFile.eof()) { myFile >> x; // Need to check again if x is valid or eof if(x) { // Do something with x } } }
#include<iostream>
#include<fstream>
using namespace std;
int main() {
   ifstream myFile("myfile.txt");
   string x;
   while(myFile >> x) {
      // Do something with x
      // No checks needed!
   }
}