概述
eof()函数是一种我们常用的判断是否读取到文件尾的类方法。
eof()函数的返回值是 bool 值。这也说明eof()函数封装的很彻底,我们无法对eof()的返回值做文章,除了简单的判断。如果读到文件尾则返回真,否则返回假。
这样来看,eof()十分简单,无非就是判断。如果返回真,说明结束了,然后结束读取即可。但是,eof函数有一点奇怪,如果你这样想,那么eof函数可能和你想的有点出入。
让我们来简单测试一下。
测试eof()函数
# include <iostream>
# include <fstream>
# include <string>
int main()
{
std::string file = "./eof_test.txt";
std::ifstream ifs;
ifs.open(file.c_str(), std::ios::in);
if(!ifs.is_open()){
std::cout << "Input.txt open failed!" << std::endl;
return -1;
}
char ch;
while (!ifs.eof()) {
ifs.get(ch);
std::cout << ch;
}
ifs.close();
return 0;
}
输出结果:

简单分析:
为什么原文件时12\n34, 而结果是12\n344?
首先,说明了while循环没有及时结束,不然不会多输出一个4的,那这显然和eof()函数脱不了关系。eof()不会如我们所想的,读完文件立刻返回真,而是有延迟,延迟了一个字符。
为什么多输出的是4,而不是其他字符呢?
首先,我们都知道文件有文件结束符,具体是什么,我们先不谈。反正,我们肯定知道,文件的结束读取,肯定和文件结束符息息相关。那简单了,eof之所以有延迟了一个字符,很可能就是因为读取了文件结束符,读取完文件结束符之后,才能判断文件已经读取完毕了。这样理解,我认为是可以接受的,但eof()内部具体是什么样,我借助vs2022看了eof()源码,但是没看出什么。
文件结束符是什么呢?我们能否借助C++把他输出来看看呢?
依据上面的理解,ifs.get(ch)语句在最后一次会读到文件结束符。但最后输出的文件结束符是4,显然文件结束符不可能是4,这是否就说明,上面我的的理解就有问题呢?其实呢,这并不冲突。有一种可能,那就是虽然读到文件结束符了,但并没有放到ch变量中,所有变量ch值并没有改变,仍然是4。简单测试一下。
}
while (!ifs.eof()) {
ifs.get(ch);
std::cout << ch;
ch = '#';
}
看运行结果,可以说明我们上述的分析是可以说的通的。
程序可能确实读到文件结束符了,但出于某种原因,又给丢弃了。所以可以说,可能有读取文件结束符的过程,但没有读取的结果,读取文件结束符并不会覆盖上次读取的结果,所以也就解释了最初的12\n34变成12\n344。
知道这个,我们就可以对最初的代码,进行一定的修改,使之能输出正确的结果。
代码改进
# include <iostream>
# include <fstream>
# include <string>
int main()
{
std::string file = "./eof_test.txt";
std::ifstream ifs;
ifs.open(file.c_str(), std::ios::in);
if (!ifs.is_open()) {
std::cout << "Input.txt open failed!" << std::endl;
return -1;
}
std::string str = "";
char ch;
while (!ifs.eof()) {
ifs.get(ch);
std::cout << ch;
ch = ' ';
}
ifs.close();
return 0;
}
虽然结果看起来和原文件一样,但我们必须明确最后4后面是有个空格的,只是看不出吧。
这就埋了个雷,我们可能会倒在这个不起眼的空格上,那我们有没有彻底解决的方案呢?自然是有的,如下:
# include <iostream>
# include <fstream>
# include <string>
int main()
{
std::string file = "./eof_test.txt";
std::ifstream ifs;
ifs.open(file.c_str(), std::ios::in);
if (!ifs.is_open()) {
std::cout << "Input.txt open failed!" << std::endl;
return -1;
}
std::string str = "";
char ch;
ifs.get(ch);
while (!ifs.eof()) {
std::cout << ch;
ifs.get(ch);
}
ifs.close();
return 0;
}