我如何读一个文件到一个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)

当前回答

#include <string>
#include <sstream>

using namespace std;

string GetStreamAsString(const istream& in)
{
    stringstream out;
    out << in.rdbuf();
    return out.str();
}

string GetFileAsString(static string& filePath)
{
    ifstream stream;
    try
    {
        // Set to throw on failure
        stream.exceptions(fstream::failbit | fstream::badbit);
        stream.open(filePath);
    }
    catch (system_error& error)
    {
        cerr << "Failed to open '" << filePath << "'\n" << error.code().message() << endl;
        return "Open fail";
    }

    return GetStreamAsString(stream);
}

用法:

const string logAsString = GetFileAsString(logFilePath);

其他回答

一种方法是将流缓冲区刷新到一个单独的内存流中,然后将其转换为std::string(错误处理省略):

std::string slurp(std::ifstream& in) {
    std::ostringstream sstr;
    sstr << in.rdbuf();
    return sstr.str();
}

这是非常简洁的。然而,正如问题中所指出的那样,这执行了冗余拷贝,不幸的是,基本上没有办法省略这个拷贝。

不幸的是,避免冗余拷贝的唯一真正解决方案是在循环中手动读取。由于c++现在保证了连续的字符串,可以编写以下代码(≥c++ 17,包含错误处理):

auto read_file(std::string_view path) -> std::string {
    constexpr auto read_size = std::size_t(4096);
    auto stream = std::ifstream(path.data());
    stream.exceptions(std::ios_base::badbit);
    
    auto out = std::string();
    auto buf = std::string(read_size, '\0');
    while (stream.read(& buf[0], read_size)) {
        out.append(buf, 0, stream.gcount());
    }
    out.append(buf, 0, stream.gcount());
    return out;
}

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

你可以使用我开发的第一个c++库来做到这一点:

#include "rst/files/file_utils.h"

std::filesystem::path path = ...;  // Path to a file.
rst::StatusOr<std::string> content = rst::ReadFile(path);
if (content.err()) {
  // Handle error.
}

std::cout << *content << ", " << content->size() << std::endl;
#include <string>
#include <fstream>

int main()
{
    std::string fileLocation = "C:\\Users\\User\\Desktop\\file.txt";
    std::ifstream file(fileLocation, std::ios::in | std::ios::binary);

    std::string data;

    if(file.is_open())
    {
        std::getline(file, data, '\0');

        file.close();
    }
}

永远不要写入std::string的const char *缓冲区。从来没有!这样做是一个巨大的错误。

在std::string中为整个字符串保留()空间,将合理大小的文件中的块读入缓冲区,然后追加()它。数据块的大小取决于输入文件的大小。我非常确定所有其他可移植的和与stl兼容的机制都会做同样的事情(但可能看起来更漂亮)。