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

当前回答

最短的变体:Live On Coliru

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

它需要头文件<iterator>。

有一些报告说,这种方法比预先分配字符串和使用std::istream::read要慢。然而,在现代的编译器上,这种情况似乎不再存在,尽管各种方法的相对性能似乎高度依赖于编译器。

其他回答

从几个地方提取信息…这应该是最快最好的方法:

#include <filesystem>
#include <fstream>
#include <string>

//Returns true if successful.
bool readInFile(std::string pathString)
{
  //Make sure the file exists and is an actual file.
  if (!std::filesystem::is_regular_file(pathString))
  {
    return false;
  }
  //Convert relative path to absolute path.
  pathString = std::filesystem::weakly_canonical(pathString);
  //Open the file for reading (binary is fastest).
  std::wifstream in(pathString, std::ios::binary);
  //Make sure the file opened.
  if (!in)
  {
    return false;
  }
  //Wide string to store the file's contents.
  std::wstring fileContents;
  //Jump to the end of the file to determine the file size.
  in.seekg(0, std::ios::end);
  //Resize the wide string to be able to fit the entire file (Note: Do not use reserve()!).
  fileContents.resize(in.tellg());
  //Go back to the beginning of the file to start reading.
  in.seekg(0, std::ios::beg);
  //Read the entire file's contents into the wide string.
  in.read(fileContents.data(), fileContents.size());
  //Close the file.
  in.close();
  //Do whatever you want with the file contents.
  std::wcout << fileContents << L" " << fileContents.size();
  return true;
}

这将宽字符读入std::wstring,但如果您只想要常规字符和std::string,则可以很容易地进行调整。

#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();
    }
}

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

#include <filesystem>
#include <fstream>
#include <string>

namespace fs = std::filesystem;

std::string readFile(fs::path path)
{
    // Open the stream to 'lock' the file.
    std::ifstream f(path, std::ios::in | std::ios::binary);

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

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

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

    return result;
}

注意:如果你的标准库还不完全支持c++ 17,你可能需要使用<experimental/filesystem>和std::experimental::filesystem。你可能还需要用&result[0]替换result.data(),如果它不支持非const std::basic_string数据。

这样的事情应该不会太糟糕:

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,然后调用下流。

#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);