当前位置:  开发笔记 > 编程语言 > 正文

在C++中将整个文件读入std :: string的最佳方法是什么?

如何解决《在C++中将整个文件读入std::string的最佳方法是什么?》经验,为你挑选了6个好方法。

如何将文件读入a std::string,即一次读取整个文件?

文本或二进制模式应由调用者指定.该解决方案应符合标准,便携且高效.它不应该不必要地复制字符串的数据,它应该避免在读取字符串时重新分配内存.

实现此目的的一种方法是统计文件大小,调整大小std::stringfread()进入std::string's const_cast()' data().这要求std::string数据是连续的,这是标准不需要的,但似乎是所有已知实现的情况.更糟糕的是,如果在文本模式下读取文件,则其std::string大小可能与文件大小不同.

一个完全正确的,符合标准的和便携式解决方案,可以构建使用std::ifstreamrdbuf()进入std::ostringstream,并从那里进入std::string.但是,这可能会复制字符串数据和/或不必要地重新分配内存.所有相关的标准库实现是否足够智能以避免所有不必要的开销?还有另一种方法吗?我是否错过了一些已经提供所需功能的隐藏Boost功能?

请显示您的建议如何实施.

void slurp(std::string& data, bool is_binary)

考虑到上面的讨论.



1> Konrad Rudol..:

并且最快(我知道,折扣内存映射文件):

std::string str(static_cast(std::stringstream() << in.rdbuf()).str());

这需要字符串流的附加标头.(这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() << in.rdbuf()).str();
}


@DevSolar嗯,更易读的版本缩短了约30%,缺少演员阵容,而且相同.因此,我的问题是:"使它成为一个问题的重点是什么?"
使它成为一个oneliner有什么意义?我总是选择清晰的代码.作为一个自称为VB.Net的爱好者(IIRC),我认为你应该理解这种情绪?
注意:此方法将文件读入stringstream的缓冲区,然后将整个缓冲区复制到`string`中.即需要两倍于其他一些选项的内存.(无法移动缓冲区).对于大文件,这将是一个重要的惩罚,甚至可能导致分配失败.
@DanNissenbaum你有点困惑.简洁性在编程中确实很重要,但实现它的正确方法是将问题分解为部分并将它们封装到独立的单元(函数,类等)中.添加功能不会减损简洁; 恰恰相反.
@sehe:我希望任何能够中途胜任的C++编码人员能够轻松理解单行代码.与周围的其他东西相比,这是相当温和的.
我知道这是非常古老的,但我只是对几种方法进行了一些分析,我发现获取文件大小并将`in.read`调用到预分配到正确大小的缓冲区比这快得多.大约10倍.我正在使用VS2012并使用100mb文件进行测试.
只是想补充一点,对于学习C++的人来说,乍一看这很难理解.
@John这就是为什么你把它放到正常的功能.对于初学者来说,大多数重要的代码都很难理解,如果这是反对使用这些代码的论据,我们就永远不会完成任何工作.

2> paxos1977..:

在类似的问题上看到这个答案.

为了您的方便,我正在重新发布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 bytes(fileSize);
    ifs.read(bytes.data(), fileSize);

    return string(bytes.data(), fileSize);
}

与针对Moby Dick(1.3M)文本的平均100次运行相比,此解决方案的执行时间比此处提供的其他答案快20%.对于便携式C++解决方案来说还不错,我希望看到mmap的文件结果;)


直到今天,我从未目睹tellg()报告非文件大小的结果.花了我几个小时才找到bug的来源.请不要使用tellg()来获取文件大小.http://stackoverflow.com/questions/22984956/tellg-function-give-wrong-size-of-file/22986486#22986486
相关:各种方法的时间性能比较:[在C++中一次读取整个文件](http://insanecoding.blogspot.ru/2011/11/reading-in-entire-file-at-once-in-c的.html)

3> Konrad Rudol..:

最短的变种: Live On Coliru

std::string str(std::istreambuf_iterator{ifs}, {});

它需要标题.

有一些报道说这种方法比预分配字符串和使用要慢std::istream::read.但是,在启用了优化的现代编译器上,似乎不再是这种情况,尽管各种方法的相对性能似乎高度依赖于编译器.


你能解决这个问题吗?它有多高效,它一次读取一个文件,无论如何要预先分配搅拌记忆?

4> Ben Collins..:

使用

#include 
#include 
#include 

int main()
{
  std::ifstream input("file.txt");
  std::stringstream sstr;

  while(input >> sstr.rdbuf());

  std::cout << sstr.str() << std::endl;
}

或者非常接近的东西.我没有打开stdlib引用来仔细检查自己.

是的,我知道我没有slurp按要求编写该功能.


为什么while循环?
while循环不应该是"while(input >> sstr.rdbuf());" ?

5> Rick Ramstet..:

我没有足够的声誉直接评论使用的回复tellg().

请注意,tellg()错误时可以返回-1.如果您将结果tellg()作为分配参数传递,则应首先检查结果.

问题的一个例子:

...
std::streamsize size = file.tellg();
std::vector buffer(size);
...

在上面的例子中,如果tellg()遇到错误,它将返回-1.签署(IE的结果之间的隐式转换tellg())和无符号(即ARG的vector构造函数)将导致您的载体错误分配一个非常大的字节数.(可能是4294967295字节,或4GB.)

修改paxos1977的答案以解释上述问题:

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    if (fileSize < 0)                             <--- ADDED
        return std::string();                     <--- ADDED

    ifs.seekg(0, ios::beg);

    vector bytes(fileSize);
    ifs.read(&bytes[0], fileSize);

    return string(&bytes[0], fileSize);
}



6> Gabriel M..:

如果你有C++ 17(std :: filesystem),也有这种方式(通过std::filesystem::file_size而不是seekg和获取文件的大小tellg):

#include 
#include 
#include 

namespace fs = std::filesystem;

std::string readFile(fs::path path)
{
    // Open the stream to 'lock' the file.
    std::ifstream f{ path };

    // Obtain the size of the file.
    const auto sz = fs::file_size(path);

    // Create a buffer.
    std::string result(sz, ' ');

    // Read the whole file into the buffer.
    f.read(result.data(), sz);

    return result;
}

注意:您可能需要使用,std::experimental::filesystem如果您的标准库尚未完全支持C++ 17.您可能还需要更换result.data()&result[0]如果它不支持非const的std :: basic_string的数据.

推荐阅读
无名有名我无名_593
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有