为什么 C++ 的文件 I/O 在读取文本文件时忽略初始空行?我怎样才能让它不这样做?

Why is C++'s file I/O ignoring initial empty lines when reading a text file? How can I make it NOT do this?

我正在尝试使用自己的自定义正则表达式和抽象语法树解析库 'srl.h'(又名“字符串和正则表达式库”)为自己构建一种小型编程语言,我发现我自己是一个我似乎不太明白的问题。

问题是这样的:当我的自定义代码遇到错误时,它显然会抛出一条错误消息,这条错误消息包含有关错误的信息,其中一位是抛出错误的行号。

问题在于 C++ 似乎完全忽略了不包含字符的行(即只是 CRLF 的行)的存在,直到它找到包含字符的行,之后点它停止忽略空行并正确对待它们,从而使所有错误抛出一个不正确的行号,它们都以相同的偏移量不正确。

基本上,如果给定一个包含内容“(crlf)(crlf)abc(crlf)def”的文件,它将被读取为好像其内容是“abc(crlf)def”,忽略初始新行,从而为抛出的所有错误报告错误的行号。

这是我用来获取文本文件文本的(各种编码混乱的)函数的副本。如果你们当中有人能告诉我这里发生了什么,那就太棒了。

template<class charT> inline std::pair<bool, std::basic_string<charT>> load_text_file(const std::wstring& file_path, const char delimiter = '\n') {


    std::ifstream fs(file_path);

    
    std::string _nl = srl::get_nlp_string<char>(srl::newline_policy);


    if (fs.is_open()) {


        std::string s;


        char b[SRL_TEXT_FILE_MAX_CHARS_PER_LINE];


        while (!fs.eof()) {


            if (s.length() > 0)
                s += _nl;


            fs.getline(b, SRL_TEXT_FILE_MAX_CHARS_PER_LINE, delimiter);


            s += std::string(b);
        }


        fs.close();


        return std::pair<bool, std::basic_string<charT>>(true, srl::string_cast<char, charT>(s));
    }
    else
        return std::pair<bool, std::basic_string<charT>>(false, std::basic_string<charT>());
}

std::ifstream::getline() 不会将定界符(在本例中为 '\n')输入到字符串中并将其从流中清除,这就是为什么文件中的所有换行符(包括领先的)在阅读时被丢弃。

程序似乎没有忽略其他行之间的换行符的原因是:

if (s.length() > 0)
     s += _nl;

所有的换行符都是从这里来的,但这不可能在一开始就发生,因为字符串是空的。

这可以用一个小测试程序来验证:

#include <iostream>
#include <fstream>
#include <string>

int main()
{
    std::ifstream inFile{ "test.txt" }; //(crlf)(crlf)(abc)(crlf)(def) inside

    char line[80]{};
    int lineCount{ 0 };

    std::string script;

    while (inFile.peek() != EOF) {
        inFile.getline(line, 80, '\n');
        lineCount++;

        script += line;
    }

    std::cout << "***Captured via getline()***" << std::endl;
    std::cout << script << std::endl; //prints "abcdef"
    std::cout << "***End***" << std::endl << std::endl;

    std::cout << "Number of lines: " << lineCount; //result: 5, so leading /n processed

}

如果去掉if条件,那么程序刚刚:

s += _nl;

,将插入换行符而不是从文件中丢弃的换行符,但只要 '\n' 是分隔符,std::ifstream::getline() 将继续丢弃原来的换行符。

最后,我建议使用

while (fs.peek() != EOF){};

而不是

while(fs){};while(!fs.eof()){};

如果你在测试程序中查看 int lineCount 的最终值,后两者给出 6 而不是 5,因为它们最终进行了冗余迭代。