使用 stringstream 逐行读取文件两次

Read a file line-by-line twice using stringstream

我需要逐行读取一个文件两次。文件内容应适合内存。所以,我通常会将整个文件读入一个缓冲区,然后再使用该缓冲区。

但是,由于我想使用 std::getline,因此我需要使用 std::basic_istream。所以,我认为写

是个好主意
std::ifstream file(filepath);
std::stringstream ss;
ss << file.rdbuf();

for (std::string line; std::getline(ss, line);)
{
}

但是,我不确定这里到底发生了什么。我猜 ss << file.rdbuf(); 确实 而不是 将文件读入 ss 的任何内部缓冲区。实际文件访问应该只发生在 std::getline(ss, line);.

因此,使用所提供形式的第二个 for 循环,我应该再次读取整个文件。那是低效的。

我是否正确,因此需要想出其他方法?

第一次循环后,清除 EOF 和失败位并返回到 stringstream 的开头:

ss.clear();
ss.seekg(0, std::ios::beg);

I guess ss << file.rdbuf(); does not read the file into any internal buffer of ss. Actual file access should occure only at std::getline(ss, line);.

这是不正确的。 cppreference.com 关于 operator<< 过载有这样的说法:

basic_ostream& operator<<( std::basic_streambuf<CharT, Traits>* sb); (9)

9) Behaves as an UnformattedOutputFunction. After constructing and checking the sentry object, checks if sb is a null pointer. If it is, executes setstate(badbit) and exits. Otherwise, extracts characters from the input sequence controlled by sb and inserts them into *this until one of the following conditions are met:

  • end-of-file occurs on the input sequence;
  • inserting in the output sequence fails (in which case the character to be inserted is not extracted);
  • an exception occurs (in which case the exception is caught).

If no characters were inserted, executes setstate(failbit). If an exception was thrown while extracting, sets failbit and, if failbit is set in exceptions(), rethrows the exception.

所以你的假设是不正确的。 file 的全部内容被复制到由 ss 控制的缓冲区,因此从 ss 读取不会访问文件系统。您可以随意通读 ss 并根据需要多次返回开头,而不会每次都产生 re-reading 文件的开销。

Am I correct and hence need to come up with an other approach?

你错了。 "hense" 也是没有根据的。问题中的信息不足,但我怀疑问题与使用流缓冲区无关。

不知道第一个 "garbage" 字符是什么,我不能肯定地说,但我怀疑文件是 wide-character unicode 格式,并且您使用的访问操作不起作用在宽字符上。如果是这样,缓冲文件与问题无关。

作为实验,请尝试以下操作。注意w。

    std::wifstream file(filepath);
    std::wstringstream ss;
    ss << file.rdbuf();

    for (int i = 0; i < 42; ++i) {
        wchar_t ch;
        ss >> ch;
        std::cout << static_cast<unsigned>(ch) << ' ';
    }

如果前四个数字是 255 254 92 0 或 255 254 47 0,我不会感到惊讶。

这可能有帮助:Problem using getline with unicode files