c++ - 使用 fstream 将完整文件读取为字符串的最佳方式?

标签 c++ file input

许多其他帖子,如“Read whole ASCII file into C++ std::string”,解释了一些选项是什么,但没有深入描述各种方法的优缺点。我想知道为什么一种方法优于另一种方法?

所有这些都使用 std::fstream 将文件读入 std::string。我不确定每种方法的成本和 yield 是多少。让我们假设这是针对已知读取文件的一些较小内存可以轻松容纳的常见情况,显然,无论您如何操作,将多 TB 文件读入内存都不是一个好主意。

经过一些谷歌搜索将整个文件读入 std::string 后,最常见的方法是使用 std::getline 并在每一行之后附加一个换行符。这对我来说似乎是不必要的,但是否有一些性能或兼容性原因认为这是理想的?

std::string Results;
std::ifstream ResultReader("file.txt");    
while(ResultReader)
{
    std::getline(ResultReader, Results);
    Results.push_back('\n');
}

我拼凑的另一种方法是更改​​ getline 定界符,使其不在文件中。 EOF 字符似乎不太可能位于文件的中间,因此这似乎是一个可能的候选者。这包括一个强制转换,因此至少有一个理由不这样做,但这确实会在没有字符串连接的情况下立即读取一个文件。据推测,分隔符检查仍然有一些成本。还有其他不这样做的充分理由吗?

std::string Results;
std::ifstream ResultReader("file.txt");
std::getline(ResultReader, Results, (char)std::char_traits<char>::eof());

转换意味着在将 std::char_traits::eof() 定义为非 -1 的系统上可能会出现问题。这是不选择它而不是使用 std::getlinestring::push_pack('\n') 的其他方法的实际原因吗?

这些与其他一次读取文件的方式相比如何,如这个问题:Read whole ASCII file into C++ std::string

std::ifstream ResultReader("file.txt");
std::string Results((std::istreambuf_iterator<char>(ResultReader)),
                     std::istreambuf_iterator<char>());

这似乎是最好的。它将几乎所有工作卸载到标准库上,标准库应该针对给定平台进行大量优化。除了流有效性和文件末尾之外,我认为没有理由进行检查。这是理想的还是存在未发现的问题。

标准或某些实现的细节是否提供了优先选择某种方法而不是另一种方法的理由?我是否错过了一些可能在各种情况下都证明是理想的方法?

将整个文件读入 std::string 的最简单、最惯用、性能最佳且符合标准的方法是什么?

编辑 - 2 这个问题促使我编写了一小套基准测试。它们是 MIT 许可证,可在 github 上获得:https://github.com/Sqeaky/CppFileToStringExperiments

最快 - TellSeekRead 和 CTellSeekRead - 这些系统提供了一种轻松获取大小并一次性读取文件的方法。

更快 - Getline Appending 和 Eof - 检查字符似乎不会产生任何成本。

快速 - RdbufMove 和 Rdbuf - std::move 似乎对发布没有影响。

- 迭代器、BackInsertIterator 和 AssignIterator - 迭代器和输入流有问题。内存中的伟大作品,但不在这里。也就是说,其中一些比其他的更快。

到目前为止,我已经添加了所有建议的方法,包括链接中的方法。如果有人可以在 Windows 和其他编译器上运行它,我将不胜感激。我目前无法访问带有 NTFS 的机器,并且已经注意到这和编译器细节可能很重要。

至于衡量简单性和惯用性,我们如何客观地衡量这些?简单化似乎是可行的,或许可以使用 LOCs 和圈复杂度,但某些东西的惯用程度似乎纯粹是主观的。

最佳答案

What is a simplest, most idiomatic, best performing and standard compliant way of reading a whole file into an std::string?

这些是非常矛盾的要求,一个最有可能减少另一个。更简单的代码不会是最快的或更惯用的。

在探索这个领域一段时间后,我得出了一些结论:
1) 造成最大性能损失的是 IO 操作本身 - 执行的 IO 操作越少 - 代码越快
2) 内存分配也相当昂贵,但不如 IO 昂贵
3) 读取二进制文件比读取文本文件更快
4) 使用 OS API 可能比 C++ 流更快
5) std::ios_base::sync_with_stdio 并没有真正影响性能,这是一个都市传说。

如果需要性能,使用 std::getline 可能不是最佳选择,因为这些原因:它将为 N 行进行 N IO 操作和 N 分配。

一种快速、标准和优雅的折衷方案是获取文件大小,一次性分配所有内存,然后一次性读取文件:

std::ifstream fileReader(<your path here>,std::ios::binary|std::ios::ate);
if (fileReader){
  auto fileSize = fileReader.tellg();
  fileReader.seekg(std::ios::beg);
  std::string content(fileSize,0);
  fileReader.read(&content[0],fileSize);
}   

四处移动内容以防止不需要的拷贝。

关于c++ - 使用 fstream 将完整文件读取为字符串的最佳方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32169936/

相关文章:

python - 这段代码中的 'wb' 是什么意思,使用 Python?

c++ - 通过 UDP 发送结构

python - 在python中将数据追加到文件末尾

C++ 包含标准 header 的保护

c - 修改用户输入文件路径以扫描C中同一目录中的文件

input - Modal Open Bootstrap 4 上的自动对焦输入元素

c - 通过 pipe() 系统调用 : how to imitate pressing enter (during the input) in terminal? 向子进程传输数据

jQuery 焦点有时最终会使闪烁的文本光标消失

c++ - 在点数组中找到最小值的算法

c++ - 将类作为函数参数传递