为什么iostream :: eof在循环条件内(即(!stream.eof())`)认为是错误的?

发布于 2025-01-26 12:21:20 字数 456 浏览 5 评论 0原文

我刚刚在回答说在循环条件下使用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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

氛圍 2025-02-02 12:21:20

因为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

Because iostream::eof will only return true 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):

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 not the same as

if(!inStream.eof())
    inFile >> data
荆棘i 2025-02-02 12:21:20

底线顶部:在正确处理空白的情况下,以下是如何使用eof(甚至比fail> 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()明确的不仅不是“ 总是错误的”,这似乎是在此和类似的堆栈中的一个偏高的意见但是,通过正确处理白色空间,它可以提供更清洁,更可靠的错误处理,并且始终是正确的解决方案(尽管不一定是束缚)。

总结为“适当”终止和阅读顺序所建议的内容如下:

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

// Which is equivalent to
while( !(in >> data).fail() )  {  /* ... */ }

以EOF超出EOF的读数为终止条件。这意味着没有简单的方法可以区分成功的流和由于EOF以外的其他原因而失败的流。进行以下流:

  • 1 2 3 4 5< eof>
  • 1 2 A 3 4 5< eof>
  • a< eof> eof>

while(in> gt; data)用set failbit for 全部三个输入终止。在第一和第三,也设置了eofbit。因此,过去的循环需要非常丑陋的额外逻辑,以将适当的输入(第一个)与不当的输入(第二和第三)区分开。

鉴于,以下内容:

while( !in.eof() )
{
   int data;
   in >> data;
   if ( in.fail() ) /* Handle with break or throw */;
   // Now use data
}

此处,in.fail()验证只要有东西要读取,它是正确的。目的不仅仅是,而循环终结器。

到目前为止还算不错,但是如果流中有尾随空间,这听起来像是eof()作为终结者的主要关注点?

我们不需要交出错误处理;只需吃白空间:

while( !in.eof() )
{
   int data;
   in >> data >> ws; // Eat white space 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
}

摘要:正确构造的,而(!数据将在范围内定位,并像往常一样提供了从业务检查的错误分离。话虽这么说,<代码>(!fail)是无疑是一个更常见的和简短的成语,并且可以在简单的(每个读取类型的单一读)方案中首选。

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):

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.)


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 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:

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:

  • 1 2 3 4 5<eof>
  • 1 2 a 3 4 5<eof>
  • a<eof>

while(in>>data) terminates with a set failbit 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:

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.

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:

while( !in.eof() )
{
   int data;
   in >> data >> ws; // Eat white space 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:

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 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.

只为守护你 2025-02-02 12:21:20

因为如果程序员不编写while(stream&gt;&gt; n),他们可能会写下此:

while(!stream.eof())
{
    stream >> n;
    //some work on n;
}

这里的问题是,您不能在没有先行的情况下进行上的某些工作检查流读取是否成功,因为如果不成功,您的n上的一些工作将产生不期望的结果。

重点是,eofbitbadbitfailbit在尝试从流中读取后,设置了。<<<<<<<<<<<<< /strong>因此,如果流&gt;&gt; n失败,然后eofbitbadbitfailbit是立即设置的,因此,如果您在编写时,则更加惯用(流&gt;&gt; n),因为返回的对象将转换为false如果从流中阅读出现故障,因此循环停止。如果读取成功并且循环继续进行,则它将转换为true 。

Because if programmers don't write while(stream >> n), they possibly write this:

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.

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.

一向肩并 2025-02-02 12:21:20

其他答案解释了为什么逻辑在中是错误的,而(!stream.eof())以及如何修复它。我想专注于不同的东西:

为什么要使用iostream :: eof错误检查EOF?

一般而言,检查eof 唯一的是错误的,因为流提取(&gt;&gt;&gt;)可能会失败而不会击中文件的结尾。如果您有Eg int n; cin&gt;&gt; n;和流包含hello,然后h不是有效的数字,因此提取将失败而不到达输入结束。

这个问题与试图从中阅读之前检查流状态的一般逻辑错误相结合,这意味着对于n个输入项目,循环将运行n+1次,导致以下症状:

  • < p>如果流为空,则循环将运行一次。 &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){...} 一样%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:

why is checking for eof explicitly using iostream::eof wrong?

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 contains hello, then h 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 (by stream >> 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 to 0 instead of leaving them uninitialized (except for chars).)

  • 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 the scanf call itself: while (scanf("%d%d", &n, &m) == 2) { ... }.

∞琼窗梦回ˉ 2025-02-02 12:21:20

要记住的重要一点是,infile.eof()不会变成true,直到 失败,因为您已经到达了文件的末尾。因此,在此示例中,您会遇到一个错误。

while (!inFile.eof()){
    inFile >> x;
        process(x);
}

使此循环正确的方法是将阅读和检查结合到一个单个操作中,例如

while (inFile >> x) 
    process(x); 

按照惯例,运营商&gt;&gt;返回我们从中读取的流,以及在流返回的布尔值测试<代码> false 流程失败时(例如,到达文件的末尾)。

因此,这给了我们正确的顺序:

  • 读取
  • 测试是否在且仅当测试成功时才能成功
  • 请处理我们阅读的内容

,请处理我们遇到的某些其他问题, 这样可以防止您正确从文件中读取,因此您将无法达到eof()。例如,让我们看一下类似的内容

int x; 
while (!inFile.eof()) { 
    inFile >> x; 
    process(x);
} 

,让我们跟踪上述代码的工作,其中一个示例

  • 假设文件的内容为'1','2','2','3','a','' B'
  • 循环将正确读取1、2和3。
  • 然后,它将进入a
  • 当它试图将a提取为int时,它将失败。
  • 现在,该流处于失败状态,直到我们clear流中,所有从中读取的尝试都将失败。
  • 但是,当我们测试eof()时,它将返回false,因为我们不在文件末尾,因为仍然有a等待读取。
  • 循环将继续尝试从文件中读取,并每次失败,因此它永不达到文件的末尾。
  • 因此,上面的循环将永远运行。

但是,如果我们使用这样的循环,我们将获得所需的输出。

while (inFile >> x)
    process(x);

在这种情况下,流将不仅在文件结束时转换为false,而且如果转换失败,例如a我们可以' t读为整数。

The important thing to remember is that inFile.eof() doesn’t become true until after an attempted read fails, because you’ve reached the end of the file. So, in this example, you’ll get an error.

while (!inFile.eof()){
    inFile >> x;
        process(x);
}

The way to make this loop correct is to combine reading and checking into a single operation, like so

while (inFile >> x) 
    process(x); 

By convention, operator>> returns the stream we read from, and a Boolean test on a stream returns false when the stream fails (such as reaching end of file).

So this gives us the correct sequence:

  • read
  • test whether the read succeeds
  • if and only if the test succeeds, process what we’ve read

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 this

int x; 
while (!inFile.eof()) { 
    inFile >> x; 
    process(x);
} 

Let us trace through the working of the above code, with an example

  • Assume the contents of the file are '1', '2', '3', 'a', 'b'.
  • The loop will read the 1, 2, and 3 correctly.
  • Then it’ll get to a.
  • When it tries to extract a as an int, it’ll fail.
  • The stream is now in a failed state, until or unless we clear the stream, all attempts at reading from it will fail.
  • But, when we test for eof(), it’ll return false, because we’re not at the end of the file, because there’s still a waiting to be read.
  • The loop will keep trying to read from the file, and fail every time, so it never reaches the end of the file.
  • So, the loop above will run forever.

But, if we use a loop like this, we will get the required output.

while (inFile >> x)
    process(x);

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 the a that we can’t read as an integer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文