从文本文件读取,直到 EOF 重复最后一行 [duplicate]

Reading from text file until EOF repeats last line [duplicate]

提问人:Ashwin Nanjappa 提问时间:8/22/2008 最后编辑:Lightness Races in OrbitAshwin Nanjappa 更新时间:9/7/2016 访问量:370521

问:

以下 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;
}

输入 .txt

10  
20  
30

输出

10  
20  
30  
30

注意:我跳过了所有错误检查代码,以保持代码片段较小。上述行为在 Windows (Visual C++)、cygwin (gcc) 和 Linux (gcc) 上可见。

C++ IOstream fstream

评论


答:

135赞 wilhelmtell 8/22/2008 #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;
}

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

评论

49赞 Martin York 9/23/2008
使用“while(iFile >> x)”。这将读取整数并返回流。当流用作布尔值时,它会检查流是否有效。有效意味着 eof() 和 bad() 都是假的。看到 stackoverflow.com/questions/21647/...
3赞 PHF 4/13/2012
与向上评论的精神相同:而不是,似乎写更好while(true)while(file.good())
2赞 sfelber 10/8/2015
如果您不确定如何读取文件,直到错误或文件结束,请阅读这篇文章(非常好的解释)gehrcke.de/2011/06/...
43赞 Patrick Loz 8/22/2008 #2

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

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

while (iFile >> x) 
{
    cerr << x << endl;
}

不知道它有多安全......

评论

0赞 harryngh 7/4/2014
如果 0 是有效值,例如 x==0 怎么办?
6赞 wchargin 5/2/2015
@harryngh如果我理解正确的话,返回的是流本身,而不是.然后,将流隐式转换为 ,用于测试 EOF。iFile >> xxbool
0赞 Martijn Courteaux 3/2/2023
非常酷的模式,因为这也适用于从流中读取多个内容,这些内容应该共同形成一个实体。喜欢:。while (file >> x_coord >> y_coord) { }
15赞 wilhelmtell 8/22/2008 #3

还有另一种方法:

#include <iterator>
#include <algorithm>

// ...

    copy(istream_iterator<int>(iFile), istream_iterator<int>(),
         ostream_iterator<int>(cerr, "\n"));
7赞 Solostaran14 2/13/2013 #4

如果不对原始代码进行太多修改,它可能会变成:

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

但总的来说,我更喜欢上面的另外两个解决方案。

评论

1赞 Bouke Versteegh 9/23/2020
我想这应该是?没有if (iFile.eof()) break;!
2赞 user1384482 4/12/2013 #5
int x;
ifile >> x

while (!iFile.eof())
{  
    cerr << x << endl;        
    iFile >> x;      
}
7赞 Brian Jack 12/3/2014 #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;
}
3赞 Raf 12/21/2014 #7

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