使用fstream读取完整文件到字符串的最佳方式
Optimal way of reading a complete file to a string using fstream?
许多其他的帖子,如"将整个ASCII文件读取为c++ std::string "解释了一些选项是什么,但没有深入描述各种方法的优缺点。我想知道为什么一种方法优于另一种方法?
所有这些都使用std::fstream
将文件读入std::string
。我不确定每种方法的成本和收益。让我们假设这是一种常见的情况,在这种情况下,读取文件的大小很小,内存可以很容易地容纳,显然,无论你怎么做,读取一个多tb的文件到内存中都是一个坏主意。
经过几次google搜索后,最常见的将整个文件读入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::getline
和string::push_pack('n')
的其他方法的实际原因吗?
这些方法与其他一次性读取文件的方法相比如何呢?
std::ifstream ResultReader("file.txt");
std::string Results((std::istreambuf_iterator<char>(ResultReader)),
std::istreambuf_iterator<char>());
这似乎是最好的。它将几乎所有的工作都转移到标准库上,而标准库应该针对给定的平台进行大量优化。除了流有效性和文件结束外,我看不出检查的理由。这是理想的吗?还是存在未被发现的问题?
标准或某些实现的细节是否提供了选择某些方法的理由?我是否错过了一些可能在各种情况下被证明是理想的方法?
将整个文件读入std::string
的最简单、最惯用、性能最好且符合标准的方法是什么?
EDIT - 2这个问题促使我编写了一套小型基准测试。它们是MIT许可的,可以在github上获得:https://github.com/Sqeaky/CppFileToStringExperiments
最快 - TellSeekRead和CTellSeekRead-这些系统提供了一个容易获得的大小和读取文件在一次。
更快 - Getline追加和Eof -检查字符似乎不施加任何成本
快速 - RdbufMove和Rdbuf - std::move在发布中似乎没有区别。
Slow -迭代器、BackInsertIterator和AssignIterator -迭代器和输入流有问题。这作品在记忆里很棒,但在这里却不行。也就是说,其中一些比其他的快。
我已经添加了到目前为止建议的所有方法,包括链接中的方法。如果有人能在windows和其他编译器上运行这个,我会很感激。我目前无法访问具有NTFS的机器,并且已经注意到这一点和编译器细节可能很重要。
至于衡量简单性和习惯性,我们如何客观地衡量它们?简单似乎是可行的,也许使用一些行loc和圈复杂度,但如何习惯的东西似乎纯粹是主观的。
什么是最简单、最习惯、最好的表现和标准将整个文件读取为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);
}
移动内容,以防止不需要的副本。
这个网站对几种不同的方法进行了很好的比较。我目前使用的是:
std::string read_sequence() {
std::ifstream f("sequence.fasta");
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
如果你的文本文件用换行符分隔,这将保留它们。例如,如果您想要删除它(这是我大多数情况下的情况),您只需添加对
之类内容的调用即可。auto s = ss.str();
s.erase(std::remove_if(s.begin(), s.end(),
[](char c) { return c == 'n'; }), s.end());
你的问题有两大难点。首先,标准没有强制要求任何特定的实现(是的,几乎每个人都从相同的实现开始;但是随着时间的推移,他们一直在修改它,比如说,NTFS的最佳I/O代码将不同于ext4的最佳I/O代码,因此有可能(尽管有些不太可能)某种特定方法在一个平台上最快,而在另一个平台上却不行。其次,定义"最优"有点困难;我猜你的意思是"最快",但事实并非如此。
有一些方法是惯用的,并且非常好的c++,但不太可能提供出色的性能。如果您的目标是最终使用单个std::string
,那么使用std::getline(std::ostream&, std::string&)
很可能会比必要时慢。std::getline()
调用必须查找'n'
,您偶尔会重新分配并复制目标std::string
。即便如此,它也非常简单,很容易理解。从维护的角度来看,这可能是最优的,假设您不需要绝对最快的性能。如果不需要一次性将整个文件保存在一个巨大的std::string
中,这也是一个很好的方法。你会非常节省内存。
一种可能更有效的方法是操作读缓冲区:
std::string read_the_whole_file(std::ostream& ostr)
{
std::ostringstream sstr;
sstr << ostr.rdbuf();
return sstr.str();
}
就我个人而言,我很可能使用std::fopen()
和std::fread()
(和std::unique_ptr<FILE>
),因为至少在Windows上,当std::fopen()
失败时,你会得到一个更好的错误消息,而不是在构建文件流对象失败时。在决定哪种方法是最佳方法时,我认为更好的错误消息是一个重要因素。
- 在 c++ 中对类中的 c 字符串动态数组进行排序的最佳方法是什么?
- 管理字符串文本的最佳做法
- 连接和压缩标准::vector<std::字符串的最佳方法>
- 将位字符串转储到二进制文件的最佳方法是什么
- 在C++中拆分字符串的最佳做法
- 当通过常量和不是字符串的最佳选择时,是否有任何情况?
- 查找字符串是否包含字符串向量的任何一个元素的最佳方法
- 当字符串中只有一个"GOOD"字符串可用时,从没有空格的字符串中提取第一个单词的最佳和有效方法?
- C++ std::string 中是否有任何函数可以计算两个字符串的相同起始字符的总数或任何最佳方法
- 总结数字字符串输入的最佳方法
- 在字符串中找到单词出现数量的最佳方法(C ,字符串中没有空格)
- 连接字符串的最佳方法是什么?
- 消除从字符串常量到'char*'的已弃用转换的最佳方法"
- 将字符串中的特定单词与c 中的if或while进行比较的最佳方法
- 根据特定数据对字符串进行排序的最佳方法是什么
- 在C 中将字符串归为字符串的最佳方法是什么?
- 将映射从字符串到INT的最佳方法,而另一个同时进行的方式
- 在C++中分隔字符串并存储到数组中的最佳方法
- 将二进制字符的字符串表示转换为字符串的最佳方法
- 只保留单词和撇号到字符串中的最佳方式