C++正确读取文件最后一行

一 问题

使用C++ ifstream来读取文件时,发现在读到文件结尾时会多读一行。测试代码如下,

#include 
#include 
#include 

int main(void)
{
    std::ifstream inf("data.txt");
    if (!inf) {
        std::cerr << "open data.txt fail\n";
        return 1;
    }

    std::string oneline;
    while (inf.eof() == false) {  // 问题点
        std::getline(inf, oneline);
		std::cout << oneline << "\n";
    }

    return 0;
}

data.txt内容如下,
C++正确读取文件最后一行_第1张图片
输出如下,多读了一行,为空。
C++正确读取文件最后一行_第2张图片


二 解决办法

方法1

直接判断std::getline()的返回值,

#include 
#include 
#include 

int main(void)
{
    std::ifstream inf("data.txt");
    if (!inf) {
        std::cerr << "open data.txt fail\n";
        return 1;
    }

    std::string oneline;
    while (std::getline(inf, oneline)) { 
		std::cout << oneline << "\n";
    }

    return 0;
}

方法2

使用文件流的peek()方法来代替eof()方法,如下,

#include 
#include 
#include 

int main(void)
{
    std::ifstream inf("data.txt");
    if (!inf) {
        std::cerr << "open data.txt fail\n";
        return 1;
    }

    std::string oneline;
    while (inf.peek() != EOF) { // 关键行
        std::getline(inf, oneline);
		std::cout << oneline << "\n";
    }

    return 0;
}

输出如下,
C++正确读取文件最后一行_第3张图片
可见最后一行得到了正确的处理。

方法3

先调用std::getline()去读取,然后先判断eof()再决定是否打印,

#include 
#include 
#include 

int main(void)
{
    std::ifstream inf("data.txt");
    if (!inf) {
        std::cerr << "open data.txt fail\n";
        return 1;
    }

    std::string oneline;
    while (true)
    {
        std::getline(inf, oneline);
        if (inf.eof() == true)
            break;
        std::cout << oneline << "\n";
    }

    return 0;
}

三 原因分析

《C++ primer 5th》17.5.2节对文件流的peek()方法描述如下,

peek返回输入流中下一个字符的副本,但不会将它从流中删除。peek返回的值仍然留在流中。

所以,通过peek()来提前预判下一个字符是不是文件结束符EOF,就可以让我们提前知道文件流是否已经到了结尾,就可以正确结束输入。

为什么eof()方法不行呢?下面来分析下,上面用于测试的data.txt,其实际结构如下,
C++正确读取文件最后一行_第4张图片
文件最后会有个EOF(End Of File)表示文件内容的结束。

当使用std::getline()读取完4444后,此时如果使用peek(),则返回值是EOF,而peek()方法的功能是预判下一个字符,由此可以知道std::getline()读完4444后,文件句柄的位置并不在EOF处,而是在EOF之前的那个字符位置上,此时使用eof()方法会返回false,然后调用std::getline()去读取EOF,就会导致错误,参数oneline就会变成空字符串,所以就会多打印一个空行。

如果有写的不对的地方,希望能留言指正,谢谢阅读。

你可能感兴趣的:(C/C++)