我如何读一个文件到一个std::字符串,即,读取整个文件一次?

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

一种方法是统计文件大小,调整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)

当前回答

Use

#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函数。

其他回答

Use

#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中为整个字符串保留()空间,将合理大小的文件中的块读入缓冲区,然后追加()它。数据块的大小取决于输入文件的大小。我非常确定所有其他可移植的和与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>());
}

这样做的好处是,我们先做了预留,这样我们就不必在读入时增加字符串。缺点是我们一个字符一个字符地做。更聪明的版本可以抓取整个read buf,然后调用下流。

就性能而言,我还没有找到比下面的代码更快的代码。

std::string readAllText(std::string const &path)
{
    assert(path.c_str() != NULL);
    FILE *stream = fopen(path.c_str(), "r");
    assert(stream != NULL);
    fseek(stream, 0, SEEK_END);
    long stream_size = ftell(stream);
    fseek(stream, 0, SEEK_SET);
    void *buffer = malloc(stream_size);
    fread(buffer, stream_size, 1, stream);
    assert(ferror(stream) == 0);
    fclose(stream);
    std::string text((const char *)buffer, stream_size);
    assert(buffer != NULL);
    free((void *)buffer);
    return text;
}