使用字符串流逐行读取文件两次

Read a file line-by-line twice using stringstream

本文关键字:两次 文件 读取 字符串 逐行      更新时间:2023-10-16

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

但是,由于我想使用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 循环,我应该再次阅读整个文件。这是低效的。

我是否正确,因此需要提出另一种方法?

我想ss << file.rdbuf();不会将文件读入任何内部ss的缓冲液。实际文件访问应仅在以下位置进行std::getline(ss, line);.

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

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

9) 表现为未格式化的输出函数。构造并检查哨兵对象后,检查sb是否为空指针。如果是,则执行setstate(badbit)并退出。否则,从sb控制的输入序列中提取字符,并将其插入*this,直到满足以下条件之一:

  • 文件结尾出现在输入序列上;
  • 在输出序列中插入失败(在这种情况下,不会提取要插入的字符);
  • 发生异常(在这种情况下,将捕获异常)。

如果未插入任何字符,则执行setstate(failbit)。如果在提取时引发异常,则设置failbit,如果在exceptions()中设置failbit异常,则重新引发异常。

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

在第一个循环之后,清除 EOF 和失败位,然后返回到stringstream的开头,如下所示:

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

我是否正确,因此需要提出另一种方法?

你说得不对。"母鸡"也是没有根据的。问题中没有足够的信息,但我怀疑问题与使用流缓冲区无关。

在不知道第一个"垃圾"字符是什么的情况下,我不能肯定地说,但我怀疑该文件是宽字符 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 25492 0 或 255 254 47 0,我不会感到惊讶。

这可能会有所帮助:将 getline 与 unicode 文件一起使用时出现问题