在C ++中将整个文件读入std :: string的最佳方法是什么?
如何将文件读入std :: string
中,即一次读取整个文件?
文本或者二进制模式应由调用方指定。该解决方案应符合标准,可移植且高效。它不应不必要地复制字符串的数据,并且应避免在读取字符串时重新分配内存。
一种方法是统计文件大小,将std :: string和fread()的大小调整为std :: string的const_cast <char *>()ed的data( )`。这就要求std :: string的数据是连续的,这不是标准要求的,但是对于所有已知的实现似乎都是这样。更糟糕的是,如果以文本模式读取文件,则std :: string的大小可能不等于文件的大小。
可以使用std :: ifstream`的rdbuf()将完全正确,符合标准的便携式解决方案构建为std :: ostringstream,然后再构建为std :: string。但是,这可能会复制字符串数据和/或者不必要地重新分配内存。所有相关的标准库实现是否足够聪明,可以避免所有不必要的开销?还有另一种方法吗?我是否错过了一些已经提供所需功能的隐藏Boost功能?
请显示建议如何实施。
void slurp(std::string& data, bool is_binary)
考虑到上面的讨论。
解决方案
使用
#include <iostream> #include <sstream> #include <fstream> int main() { std::ifstream input("file.txt"); std::stringstream sstr; while(input >> sstr.rdbuf()); std::cout << sstr.str() << std::endl; }
或者非常接近的东西。我没有打开stdlib参考资料来仔细检查自己。
是的,我知道我没有按照要求编写slurp
函数。
切勿写入std :: string的const char *缓冲区。永远不能!这样做是一个巨大的错误。
在std :: string中为整个字符串保留()空间,从文件中将适当大小的块读取到缓冲区中,然后执行append()。块必须有多大取决于输入文件的大小。我敢肯定,所有其他可移植且符合STL的机制都将执行相同的操作(但可能看起来更漂亮)。
最短的变体:" Live on Coliru"
std::string str(std::istreambuf_iterator<char>{ifs}, {});
它需要头文件" <iterator>"。
有报道说这种方法比预分配字符串和使用std :: istream :: read
要慢。但是,在启用了优化的现代编译器上,情况似乎不再如此,尽管各种方法的相对性能似乎高度依赖于编译器。
这样的事情应该不会太糟:
void slurp(std::string& data, const std::string& filename, bool is_binary) { std::ios_base::openmode openmode = ios::ate | ios::in; if (is_binary) openmode |= ios::binary; ifstream file(filename.c_str(), openmode); data.clear(); data.reserve(file.tellg()); file.seekg(0, ios::beg); data.append(istreambuf_iterator<char>(file.rdbuf()), istreambuf_iterator<char>()); }
这样做的好处是我们首先进行了保留,因此在读入内容时不必增加字符串。缺点是我们逐个字符地进行处理。一个更聪明的版本可以获取整个读取的buf,然后调用下溢。
我们可以使用'std :: getline'函数,并指定'eof'作为分隔符。结果代码有点晦涩:
std::string data; std::ifstream in( "test.txt" ); std::getline( in, data, std::string::traits_type::to_char_type( std::string::traits_type::eof() ) );
最快(据我所知,对内存映射文件进行打折):
std::string str(static_cast<std::stringstream const&>(std::stringstream() << in.rdbuf()).str());
这需要为字符串流添加头文件<sstream>。
(必须使用" static_cast",因为" operator <<"会返回一个普通的旧" ostream&",但是我们知道实际上它是一个" stringstream&",因此强制转换是安全的。)
分成多行,将临时变量移动到变量中,我们得到了更具可读性的代码:
std::string slurp(std::ifstream& in) { std::stringstream sstr; sstr << in.rdbuf(); return sstr.str(); }
或者,再次在一行中:
std::string slurp(std::ifstream& in) { return static_cast<std::stringstream const&>(std::stringstream() << in.rdbuf()).str(); }
看到类似问题的答案。
为了方便,我重新发布了CTT的解决方案:
string readFile2(const string &fileName) { ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate); ifstream::pos_type fileSize = ifs.tellg(); ifs.seekg(0, ios::beg); vector<char> bytes(fileSize); ifs.read(bytes.data(), fileSize); return string(bytes.data(), fileSize); }
当与Moby Dick(1.3M)进行平均100次运行时,此解决方案的执行时间比此处提供的其他答案快20%。对于便携式C ++解决方案来说还不错,我想看看mmap'ing文件的结果;)