C++ 为什么 iostream::eof 在循环条件(即`while (!stream.eof())`)中被认为是错误的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5605125/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
提问by MAK
I just found a comment in thisanswer saying that using iostream::eof
in a loop condition is "almost certainly wrong". I generally use something like while(cin>>n)
- which I guess implicitly checks for EOF.
我刚刚在这个答案中发现了一条评论,说iostream::eof
在循环条件中使用“几乎肯定是错误的”。我通常使用类似的东西while(cin>>n)
- 我猜这会隐式检查 EOF。
Why is checking for eof explicitly using while (!cin.eof())
wrong?
为什么显式地检查 eof 是while (!cin.eof())
错误的?
How is it different from using scanf("...",...)!=EOF
in C (which I often use with no problems)?
它与scanf("...",...)!=EOF
在 C 中使用(我经常使用没有问题)有什么不同?
采纳答案by Xeo
Because iostream::eof
will only return true
afterreading the end of the stream. It does notindicate, that the next read will be the end of the stream.
因为iostream::eof
只有true
在读取流结束后才会返回。它并没有表明,下一次读取将是流的末尾。
Consider this (and assume then next read will be at the end of the 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
}
Against this:
反对这一点:
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)
}
And on your second question: Because
关于你的第二个问题:因为
if(scanf("...",...)!=EOF)
is the same as
是相同的
if(!(inStream >> data).eof())
and notthe same as
并且不一样
if(!inStream.eof())
inFile >> data
回答by sly
Bottom-line top:With proper handling of white-space, the following is how eof
can be used (and even, be more reliable than fail()
for error checking):
底线顶部:通过正确处理空白,以下是如何eof
使用(甚至比fail()
错误检查更可靠):
while( !(in>>std::ws).eof() ) {
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
(Thanks Tony D for the suggestion to highlight the answer. See his comment below for an example to why this is more robust.)
(感谢托尼 D 提出突出答案的建议。请参阅下面他的评论,了解为什么这更强大的示例。)
The main argument against using eof()
seems to be missing an important subtlety about the role of white space. My proposition is that, checking eof()
explicitly is not only not "always wrong" -- which seems to be an overriding opinion in this and similar SO threads --, but with proper handling of white-space, it provides for a cleaner and more reliable error handling, and is the always correctsolution (although, not necessarily the tersest).
反对使用的主要论点eof()
似乎缺少关于空白角色的重要微妙之处。我的主张是,eof()
明确检查不仅不是“总是错误的”——这在这个和类似的 SO 线程中似乎是压倒一切的意见——而且通过正确处理空白,它提供了一个更干净、更可靠的错误处理,并且是始终正确的解决方案(尽管不一定是最简洁的)。
To summarize what is being suggested as the "proper" termination and read order is the following:
总结一下被建议为“正确”终止和读取顺序的内容如下:
int data;
while(in >> data) { /* ... */ }
// which is equivalent to
while( !(in >> data).fail() ) { /* ... */ }
The failure due to read attempt beyond eof is taken as the termination condition. This means is that there is no easy way to distinguish between a successful stream and one that really fails for reasons other than eof. Take the following streams:
以超过 eof 的读尝试失败作为终止条件。这意味着没有简单的方法可以区分成功的流和由于 eof 以外的原因而真正失败的流。取以下流:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
terminates with a set failbit
for allthree input. In the first and third, eofbit
is also set. So past the loop one needs very ugly extra logic to distinguish a proper input (1st) from improper ones (2nd and 3rd).
while(in>>data)
failbit
以所有三个输入的集合终止。在第一和第三,eofbit
也有设定。因此,通过循环需要非常丑陋的额外逻辑来区分正确的输入(第 1 次)和不正确的输入(第 2 次和第 3 次)。
Whereas, take the following:
鉴于,采取以下措施:
while( !in.eof() )
{
int data;
in >> data;
if ( in.fail() ) /* handle with break or throw */;
// now use data
}
Here, in.fail()
verifies that as long as there is something to read, it is the correct one. It's purpose is not a mere while-loop terminator.
在这里,in.fail()
验证只要有东西要读,就是正确的。它的目的不仅仅是一个 while 循环终止符。
So far so good, but what happens if there is trailing space in the stream -- what sounds like the major concern against eof()
as terminator?
到目前为止一切顺利,但如果流中有尾随空间会发生什么——听起来像eof()
作为终结者的主要问题是什么?
We don't need to surrender our error handling; just eat up the white-space:
我们不需要放弃我们的错误处理;只是吃掉空白:
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
skips any potential (zero or more) trailing space in the stream while setting the eofbit
, and not the failbit
. So, in.fail()
works as expected, as long as there is at least one data to read. If all-blank streams are also acceptable, then the correct form is:
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
}
Summary:A properly constructed while(!eof)
is not only possible and not wrong, but allows data to be localized within scope, and provides a cleaner separation of error checking from business as usual. That being said, while(!fail)
is inarguably a more common and terse idiom, and may be preferred in simple (single data per read type of) scenarios.
简介:正确构造while(!eof)
不仅是可能的而且没有错误,而且允许数据在范围内本地化,并提供更清晰的错误检查与常规业务的分离。话虽如此,这while(!fail)
无疑是一种更常见和简洁的习语,在简单(每个读取类型的单个数据)场景中可能更受欢迎。
回答by Nawaz
Because if programmers don't write while(stream >> n)
, they possibly write this:
因为如果程序员不写while(stream >> n)
,他们可能会这样写:
while(!stream.eof())
{
stream >> n;
//some work on n;
}
Here the problem is, you cannot do some work on n
without first checking if the stream read was successful, because if it was unsuccessful, your some work on n
would produce undesired result.
这里的问题是,你不能不some work on n
首先检查流读取是否成功,因为如果不成功,你some work on n
会产生不想要的结果。
The whole point is that, eofbit
, badbit
, or failbit
are set after an attempt is made to read from the stream.So if stream >> n
fails, then eofbit
, badbit
, or failbit
is set immediately, so its more idiomatic if you write while (stream >> n)
, because the returned object stream
converts to false
if there was some failure in reading from the stream and consequently the loop stops. And it converts to true
if the read was successful and the loop continues.
重点是 , eofbit
, badbit
, orfailbit
是在尝试从流中读取后设置的。因此,如果stream >> n
失败,则eofbit
, badbit
, 或failbit
立即设置,因此如果您编写 则更惯用while (stream >> n)
,因为返回的对象stream
转换为false
如果从流中读取失败并因此循环停止。true
如果读取成功并且循环继续,它会转换为。
回答by melpomene
The other answers have explained why the logic is wrong in while (!stream.eof())
and how to fix it. I want to focus on something different:
其他答案已经解释了为什么逻辑错误while (!stream.eof())
以及如何解决它。我想专注于不同的东西:
why is checking for eof explicitly using
iostream::eof
wrong?
为什么明确使用
iostream::eof
错误检查 eof ?
In general terms, checking for eof
onlyis wrong because stream extraction (>>
) can fail without hitting the end of the file. If you have e.g. int n; cin >> n;
and the stream contains hello
, then h
is not a valid digit, so extraction will fail without reaching the end of the input.
一般而言,eof
只检查是错误的,因为流提取 ( >>
) 可能会在没有到达文件末尾的情况下失败。如果您有 egint n; cin >> n;
并且流包含hello
,则h
不是有效数字,因此提取将失败而不会到达输入的末尾。
This issue, combined with the general logic error of checking the stream state beforeattempting to read from it, which means for N input items the loop will run N+1 times, leads to the following symptoms:
这个问题,结合在尝试读取流之前检查流状态的一般逻辑错误,这意味着对于 N 个输入项,循环将运行 N+1 次,导致以下症状:
If the stream is empty, the loop will run once.
>>
will fail (there is no input to be read) and all variables that were supposed to be set (bystream >> x
) are actually uninitialized. This leads to garbage data being processed, which can manifest as nonsensical results (often huge numbers).(If your standard library conforms to C++11, things are a bit different now: A failed
>>
now sets numeric variables to0
instead of leaving them uninitialized (except forchar
s).)If the stream is not empty, the loop will run again after the last valid input. Since in the last iteration all
>>
operations fail, variables are likely to keep their value from the previous iteration. This can manifest as "the last line is printed twice" or "the last input record is processed twice".(This should manifest a bit differently since C++11 (see above): Now you get a "phantom record" of zeroes instead of a repeated last line.)
If the stream contains malformed data but you only check for
.eof
, you end up with an infinite loop.>>
will fail to extract any data from the stream, so the loop spins in place without ever reaching the end.
如果流为空,则循环将运行一次。
>>
将失败(没有要读取的输入)并且所有应该设置(bystream >> x
)的变量实际上都未初始化。这会导致处理垃圾数据,这可能表现为无意义的结果(通常是巨大的数字)。(如果您的标准库符合 C++11,现在情况有点不同:A failed
>>
now 将数字变量设置为0
而不是让它们未初始化(char
s除外)。)如果流不为空,循环将在最后一个有效输入后再次运行。由于在最后一次迭代中所有
>>
操作都失败了,变量很可能会保留上一次迭代中的值。这可以表现为“最后一行打印两次”或“最后输入的记录被处理两次”。(这应该从 C++11 开始有点不同(见上文):现在你得到一个零的“幻像记录”,而不是重复的最后一行。)
如果流包含格式错误的数据,但您只检查
.eof
,则会导致无限循环。>>
将无法从流中提取任何数据,因此循环在原地旋转而不会到达终点。
To recap: The solution is to test the success of the >>
operation itself, not to use a separate .eof()
method: while (stream >> n >> m) { ... }
, just as in C you test the success of the scanf
call itself: while (scanf("%d%d", &n, &m) == 2) { ... }
.
总结一下:解决方案是测试>>
操作本身是否成功,而不是使用单独的.eof()
方法:while (stream >> n >> m) { ... }
,就像在 C 中测试scanf
调用本身是否成功一样:while (scanf("%d%d", &n, &m) == 2) { ... }
。