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!
}
}