【问题标题】:Reading from text file until EOF repeats last line [duplicate]从文本文件读取直到EOF重复最后一行[重复]
【发布时间】:2010-09-06 12:06:48
【问题描述】:

以下 C++ 代码使用 ifstream 对象从文本文件(每行一个数字)中读取整数,直到到达 EOF。为什么它读取最后一行的整数两次?如何解决这个问题?

代码:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ifstream iFile("input.txt");    // input.txt has integers, one per line

    while (!iFile.eof())
    {
        int x;
        iFile >> x;
        cerr << x << endl;
    }

    return 0;
}

input.txt:

10  
20  
30

输出:

10  
20  
30  
30

注意:我已跳过所有错误检查代码以保持代码 sn-p 小。在 Windows (Visual C++)、cygwin (gcc) 和 Linux (gcc) 上可以看到上述行为。

【问题讨论】:

    标签: c++ iostream fstream


    【解决方案1】:

    只要密切关注事件链。

    • 抓10
    • 抢20
    • 抓30
    • 抓住EOF

    查看倒数第二次迭代。你抓住了 30,然后继续检查 EOF。您尚未达到 EOF,因为尚未读取 EOF 标记(“二进制”而言,它的概念位置就在 30 行之后)。因此,您继续进行下一次迭代。 x 仍然是前一次迭代的 30。现在您从流中读取并获得 EOF。 x 保持 30 并且 ios::eofbit 被提升。你输出到 stderr x (这是 30,就像在之前的迭代中一样)。接下来检查循环条件中的 EOF,这一次您退出了循环。

    试试这个:

    while (true) {
        int x;
        iFile >> x;
        if( iFile.eof() ) break;
        cerr << x << endl;
    }
    

    顺便说一句,您的代码中还有另一个错误。你有没有试过在一个空文件上运行它?你得到的行为是出于完全相同的原因。

    【讨论】:

    • 使用'while(iFile >> x)'。这将读取整数并返回流。当流用作布尔值时,它会检查流是否有效。有效的意思是 eof() 和 bad() 都是假的。见stackoverflow.com/questions/21647/…
    • 和up评论一样的精神:不要写while(true),写while(file.good())似乎更好
    • 如果您在错误或文件结尾之前不确定如何读取文件,请阅读这篇文章(非常好的解释)gehrcke.de/2011/06/…
    【解决方案2】:

    我喜欢这个例子,它现在省略了你可以在 while 块中添加的检查:

    ifstream iFile("input.txt");        // input.txt has integers, one per line
    int x;
    
    while (iFile >> x) 
    {
        cerr << x << endl;
    }
    

    不确定它有多安全......

    【讨论】:

    • 如果 0 是一个有效值,比如 x==0 怎么办?
    • @harryngh 如果我理解正确,iFile &gt;&gt; x 返回流本身,而不是 x。然后流被隐式转换为bool,用于测试EOF。
    【解决方案3】:

    有一种替代方法:

    #include <iterator>
    #include <algorithm>
    
    // ...
    
        copy(istream_iterator<int>(iFile), istream_iterator<int>(),
             ostream_iterator<int>(cerr, "\n"));
    

    【讨论】:

      【解决方案4】:

      无需对原代码做太多修改,就可以变成:

      while (!iFile.eof())
      {  
          int x;
          iFile >> x;
          if (!iFile.eof()) break;
          cerr << x << endl;
      }
      

      但总的来说,我更喜欢上面的其他两种解决方案。

      【讨论】:

      • 我想这应该是if (iFile.eof()) break;?没有!
      【解决方案5】:
      int x;
      ifile >> x
      
      while (!iFile.eof())
      {  
          cerr << x << endl;        
          iFile >> x;      
      }
      

      【讨论】:

        【解决方案6】:

        EOF 模式需要一个主要读取来“引导”EOF 检查过程。考虑到空文件最初不会在第一次读取之前设置其 EOF。在这种情况下,主要读取将捕获 EOF 并正确地完全跳过循环。

        您需要记住的是,在第一次尝试读取文件的可用数据之前,您不会获得 EOF。读取确切的数据量不会标记 EOF。

        我应该指出,如果文件为空,您的给定代码会打印出来,因为 EOF 会阻止在进入循环时将值设置为 x。

        • 0

        所以添加一个素数读取并将循环的读取移动到末尾:

        int x;
        
        iFile >> x; // prime read here
        while (!iFile.eof()) {
            cerr << x << endl;
            iFile >> x;
        }
        

        【讨论】:

          【解决方案7】:

          在最后一行的末尾,你有一个换行符,它不是由 >> 运算符读取的,它不是文件的结尾。 请做一个实验并删除新行(文件中的最后一个字符) - 你不会得到重复。 要拥有灵活的代码并避免不必要的影响,只需应用其他用户提供的任何解决方案即可。

          【讨论】:

            猜你喜欢
            • 2013-08-17
            • 2019-05-26
            • 2023-03-09
            • 1970-01-01
            • 2013-03-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多