从文本文件读取直到 EOF 重复最后一行[重复]
题
这个问题在这里已经有答案了:
下列 C++ 代码使用 如果流 对象从文本文件(每行一个数字)读取整数,直到它命中 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) 上。
解决方案
只需密切关注事件链即可。
- 抢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 块内添加的检查:
ifstream iFile("input.txt"); // input.txt has integers, one per line
int x;
while (iFile >> x)
{
cerr << x << endl;
}
不确定它有多安全......
还有一种替代方法:
#include <iterator>
#include <algorithm>
// ...
copy(istream_iterator<int>(iFile), istream_iterator<int>(),
ostream_iterator<int>(cerr, "\n"));
EOF 模式需要一次主要读取来“引导”EOF 检查过程。考虑到空文件最初不会设置 EOF,直到第一次读取。在这种情况下,主读将捕获 EOF 并正确地完全跳过循环。
这里需要记住的是,直到第一次尝试读取文件的可用数据之前,您不会得到 EOF。读取确切的数据量不会标记 EOF。
我应该指出,如果文件为空,您将打印给定的代码,因为 EOF 将阻止在进入循环时将值设置为 x。
- 0
因此,添加一个素数读取并将循环的读取移动到末尾:
int x;
iFile >> x; // prime read here
while (!iFile.eof()) {
cerr << x << endl;
iFile >> x;
}
无需对原始代码进行太多修改,它可能会变成:
while (!iFile.eof())
{
int x;
iFile >> x;
if (!iFile.eof()) break;
cerr << x << endl;
}
但总的来说我更喜欢上面的另外两个解决方案。
int x;
ifile >> x
while (!iFile.eof())
{
cerr << x << endl;
iFile >> x;
}
在最后一行的末尾,有一个新行字符,>> 运算符不会读取该字符,并且它不是文件结尾。请进行实验并删除新行(文件中的最后一个字符) - 您将不会得到重复。要拥有灵活的代码并避免不必要的影响,只需应用其他用户提供的任何解决方案即可。