概述
eof()函数是一种我们常用的判断是否读取到文件尾的类方法。
eof()函数的返回值是 bool 值。这也说明eof()函数封装的很彻底,我们无法对eof()的返回值做文章,除了简单的判断。如果读到文件尾则返回真,否则返回假。
这样来看,eof()十分简单,无非就是判断。如果返回真,说明结束了,然后结束读取即可。但是,eof函数有一点奇怪,如果你这样想,那么eof函数可能和你想的有点出入。
让我们来简单测试一下。
测试eof()函数
//读取eof_test.txt, 文件内容如下:
// 12
// 34
# 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); // 读取模式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; //输出字符
}
//替换成
while (!ifs.eof()) { //读取文件,输出
ifs.get(ch); //一次读取一个字符
std::cout << ch; //输出字符
ch = '#'; //将ch用字符'#'做伪清空
}
//运行结果
//12
//34#
看运行结果,可以说明我们上述的分析是可以说的通的。
程序可能确实读到文件结束符了,但出于某种原因,又给丢弃了。所以可以说,可能有读取文件结束符的过程,但没有读取的结果,读取文件结束符并不会覆盖上次读取的结果,所以也就解释了最初的12\n34变成12\n344。
知道这个,我们就可以对最初的代码,进行一定的修改,使之能输出正确的结果。
代码改进
// 改进1
# 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;
}
//输出结果
//12
//34
虽然结果看起来和原文件一样,但我们必须明确最后4后面是有个空格的,只是看不出吧。
这就埋了个雷,我们可能会倒在这个不起眼的空格上,那我们有没有彻底解决的方案呢?自然是有的,如下:
// 改进2
# 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.get(ch); //和std::cout << ch;交换了位置
}
ifs.close();
return 0;
}