为什么iostream :: eof在循环条件内(即(!stream.eof())`)认为是错误的?
我刚刚在此回答说使用iostream :: eof
在循环中,条件“几乎肯定是错误的”。我通常使用(cin>> n)的之类的东西 - 我想它会隐含地检查EOF。
为什么在(!cin.eof())错误的情况下明确检查EOF?
它与C中使用scanf(“ ...”,...)!= eof
(我经常没有问题)有何不同?
I just found a comment in this answer 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.
Why is checking for eof explicitly using while (!cin.eof())
wrong?
How is it different from using scanf("...",...)!=EOF
in C (which I often use with no problems)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
因为
iostream :: eof
将仅在读取流的结尾后才返回true
。它确实不是表明,下一个读取将是流的末尾。考虑一下(并假设下一个读取将在流的末尾):
反对这一点:
在您的第二个问题上
因为
:
Because
iostream::eof
will only returntrue
after reading the end of the stream. It does not indicate, that the next read will be the end of the stream.Consider this (and assume then next read will be at the end of the stream):
Against this:
And on your second question: Because
is the same as
and not the same as
底线顶部:在正确处理空白的情况下,以下是如何使用
eof
(甚至比fail> fail()更可靠代码>以进行错误检查):(
感谢Tony D的建议,以突出显示答案。请参阅下面的评论,以说明为什么更强大。)
主要参数反对使用
eof ()
似乎是缺少关于空白的重要作用的重要微妙之处。我的主张是,检查eof()
明确的不仅不是“ 总是错误的”,这似乎是此和类似的堆栈中的倒数作用,但是,通过正确处理白色空间,它可以提供更清洁,更可靠的错误处理,并且始终是正确的解决方案(尽管不一定是束缚)。总结为“适当”终止和阅读顺序所建议的内容如下:
以EOF超出EOF的读数为终止条件。这意味着没有简单的方法可以区分成功的流和由于EOF以外的其他原因而失败的流。进行以下流:
1 2 3 4 5< eof>
1 2 A 3 4 5< eof>
a< eof> eof>
while(in> gt; data)
用setfailbit
for 全部三个输入终止。在第一和第三,也设置了eofbit
。因此,过去的循环需要非常丑陋的额外逻辑,以将适当的输入(第一个)与不当的输入(第二和第三)区分开。鉴于,以下内容:
此处,
in.fail()
验证只要有东西要读取,它是正确的。目的不仅仅是,而循环终结器。到目前为止还算不错,但是如果流中有尾随空间,这听起来像是
eof()
作为终结者的主要关注点?我们不需要交出错误处理;只需吃白空间:
std :: ws
在设置eofbit
时跳过流中的任何潜力(零或更多),而不是failbit
。因此,只要至少有一个数据要读取,in.fail()
就可以按预期工作。如果也可以接受全义流,则正确的形式是:摘要:正确构造的
,而(!数据将在范围内定位,并像往常一样提供了从业务检查的错误分离。话虽这么说,而
(!fail)
无疑是一个更常见的和简短的成语,并且可以在简单的(每个读取类型的单一读)方案中首选。Bottom-line top: With proper handling of white space, the following is how
eof
can be used (and even, be more reliable thanfail()
for error checking):(Thanks Tony D for the suggestion to highlight the answer. See his comment below for an example to why this is more robust.)
The main argument against using
eof()
seems to be missing an important subtlety about the role of white space. My proposition is that, checkingeof()
explicitly is not only not "always wrong"—which seems to be an overriding opinion in this and similar Stack Overflow questions—, but with proper handling of white-space, it provides for a cleaner and more reliable error handling, and is the always correct solution (although, not necessarily the tersest).To summarize what is being suggested as the "proper" termination and read order is the following:
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:
1 2 3 4 5<eof>
1 2 a 3 4 5<eof>
a<eof>
while(in>>data)
terminates with a setfailbit
for all three 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 (first) from improper ones (second and third).Whereas, take the following:
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.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?We don't need to surrender our error handling; just eat up the white-space:
std::ws
skips any potential (zero or more) trailing space in the stream while setting theeofbit
, and not thefailbit
. 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:Summary: A properly constructed
while(!eof)
is not only possible and not wrong, but it 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(stream&gt;&gt; n)
,他们可能会写下此:这里的问题是,您不能在没有先行的情况下进行上的某些工作检查流读取是否成功,因为如果不成功,您的
n
上的一些工作将产生不期望的结果。重点是,
eofbit
,badbit
或failbit
在尝试从流中读取后,设置了。<<<<<<<<<<<<< /strong>因此,如果流&gt;&gt; n
失败,然后eofbit
,badbit
或failbit
是立即设置的,因此,如果您在编写时,则更加惯用(流&gt;&gt; n)
,因为返回的对象流
将转换为false
如果从流中阅读出现故障,因此循环停止。如果读取成功并且循环继续进行,则它将转换为true 。Because if programmers don't write
while(stream >> n)
, they possibly write this: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, yoursome work on n
would produce undesired result.The whole point is that,
eofbit
,badbit
, orfailbit
are set after an attempt is made to read from the stream. So ifstream >> n
fails, theneofbit
,badbit
, orfailbit
is set immediately, so its more idiomatic if you writewhile (stream >> n)
, because the returned objectstream
converts tofalse
if there was some failure in reading from the stream and consequently the loop stops. And it converts totrue
if the read was successful and the loop continues.其他答案解释了为什么逻辑在
中是错误的,而(!stream.eof())
以及如何修复它。我想专注于不同的东西:一般而言,检查
eof
唯一的是错误的,因为流提取(&gt;&gt;&gt;
)可能会失败而不会击中文件的结尾。如果您有Egint n; cin&gt;&gt; n;
和流包含hello
,然后h
不是有效的数字,因此提取将失败而不到达输入结束。这个问题与试图从中阅读之前检查流状态的一般逻辑错误相结合,这意味着对于n个输入项目,循环将运行n+1次,导致以下症状:
&gt;&gt;
将失败(没有要读取的输入),并且所有应该设置的变量(通过stream&gt;&gt; x
)均为非机密化。这会导致处理垃圾数据,这可能表现为荒谬的结果(通常数量很大)。(如果您的标准库符合C ++ 11,那么现在情况有所不同:一个失败的
&gt;&gt;
现在将数字变量设置为0
而不是使它们非专业化(char
s除外)如果流不为空,则循环将在最后一个有效输入后再次运行。由于在上次迭代中,所有
&gt;&gt;
操作失败,因此变量可能会从上一个迭代中保持其价值。这可以表现为“最后一行被打印两次”或“最后一个输入记录被处理两次”。(由于C ++ 11,这应该有所不同(请参见上文):现在您获得了零的“幻影记录”,而不是重复的最后一行。)
如果该流包含错误的数据,但是您只检查<<<<代码> .eof ,您最终会获得无限循环。
&gt;&gt;
将无法从流中提取任何数据,因此循环旋转而无需达到末端。回顾:解决方案是测试
&gt;&gt;
操作本身的成功,不使用单独&gt;&gt; n&gt;&gt; m){...} ,就像在(scanf(“%d%d”,&amp; n,&amp; m)== 2){...} 。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:In general terms, checking for
eof
only is 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 containshello
, thenh
is not a valid digit, so extraction will fail without reaching the end of the input.This issue, combined with the general logic error of checking the stream state before attempting to read from it, which means for N input items the loop will run N+1 times, leads to the following symptoms:
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.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 thescanf
call itself:while (scanf("%d%d", &n, &m) == 2) { ... }
.要记住的重要一点是,
infile.eof()
不会变成true
,直到 失败,因为您已经到达了文件的末尾。因此,在此示例中,您会遇到一个错误。使此循环正确的方法是将阅读和检查结合到一个单个操作中,例如
按照惯例,
运营商&gt;&gt;
返回我们从中读取的流,以及在流返回的布尔值测试<代码> false 流程失败时(例如,到达文件的末尾)。因此,这给了我们正确的顺序:
请处理我们遇到的某些其他问题, 请处理我们阅读的内容这样可以防止您正确从文件中读取,因此您将无法达到
eof()
。例如,让我们看一下类似的内容,让我们跟踪上述代码的工作,其中一个示例
'1','2','2','3','a','' B'
。a
。a
提取为int时,它将失败。clear
流中,所有从中读取的尝试都将失败。false
,因为我们不在文件末尾,因为仍然有a
等待读取。但是,如果我们使用这样的循环,我们将获得所需的输出。
在这种情况下,流将不仅在文件结束时转换为
false
,而且如果转换失败,例如a
我们可以' t读为整数。The important thing to remember is that
inFile.eof()
doesn’t becometrue
until after an attempted read fails, because you’ve reached the end of the file. So, in this example, you’ll get an error.The way to make this loop correct is to combine reading and checking into a single operation, like so
By convention,
operator>>
returns the stream we read from, and a Boolean test on a stream returnsfalse
when the stream fails (such as reaching end of file).So this gives us the correct sequence:
If you happen to encounter some other problem that prevents you from reading from the file correctly, you will not be able to reach
eof()
as such. For example, let’s look at something like thisLet us trace through the working of the above code, with an example
'1', '2', '3', 'a', 'b'
.a
.a
as an int, it’ll fail.clear
the stream, all attempts at reading from it will fail.false
, because we’re not at the end of the file, because there’s stilla
waiting to be read.But, if we use a loop like this, we will get the required output.
In this case, the stream will convert to
false
not only in case of end of file, but also in case of a failed conversion, such as thea
that we can’t read as an integer.