为什么将文件读取到 std::string 在 Windows 上长度错误?

Why does reading a file to a std::string have a wrong length on Windows?

我已经尝试了各种惯用的 C++ 解决方案,用于在 Windows 上将文件读入 std::string,自从 std::string 以来,它一直让我头疼不已长度一直是错误的(例如比原始文件小得多)。不过,相同的代码在 Linux 上都能完美运行。

以下函数在 Windows 上都表现不正常:

#include <boost/filesystem.hpp>
#include <iostream>
#include <sstream>

using namespace boost::filesystem;
using namespace boost::iostreams;

std::string read_string_from_file_using_streams(const path &file_path) {
    return read_string_from_file_using_streams(file_path.parent_path(), file_path.filename().string());
}

std::string read_string_from_file_using_streams(const path &parent_directory, const std::string &file_name) {
    const auto original_file_path =
            parent_directory.string() + (char) path::preferred_separator + file_name;
    const std::ifstream input_stream(original_file_path);

    if (input_stream.fail()) {
        throw std::runtime_error("File is not readable");
    }

    std::stringstream buffer;
    buffer << input_stream.rdbuf(); // Does not read the whole file on Windows!

    return buffer.str();
}

std::string read_string_from_file_with_string_pre_allocation(const std::string& path) {
    std::ifstream t(path);
    std::string str;

    t.seekg(0, std::ios::end);
    str.reserve(t.tellg()); // Reserves the correct length
    t.seekg(0, std::ios::beg);

    str.assign(std::istreambuf_iterator<char>(t), // Does not read the whole file on Windows!
        std::istreambuf_iterator<char>());

    return str;
}

std::string read_string_from_file_using_if_stream(const std::string& path) {
    std::ifstream file(path);
    return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()); // Does not read the whole file on Windows!
}

最后,以下 C 标准代码示例可以正常工作:

std::string read_using_c_standard_library(const std::string& path) {
    auto f = fopen(path.c_str(), "rb");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    auto string = (char *) malloc(fsize + 1);
    fread(string, 1, fsize, f); // This reads the whole file on Windows, finally!
    fclose(f);

    string[fsize] = 0;

    std::string s(string, fsize);
    free(string);
    return s;
}

我不明白为什么会这样。 C++ 代码方法有什么问题?我的示例文件是 516 KB 并且错误函数仅读取例如912 bytes.

你能看出区别吗?

const std::ifstream input_stream(original_file_path);

auto f = fopen(path.c_str(), "rb");

在第二种情况下文件以二进制模式打开,在第一种情况下不是。

也就是说,代码仍应读取整个文件,但它会更改行结尾,因此生成的字符串的大小可能不是您所期望的。

在Windows上,文本文件的行尾通常是\r\n,在Linux上它们只是\n

以文本模式打开文件会导致 \r\n 行结尾转换为 \n,因此它们在两个平台上都是 \n

然而,这应该只对文本文件执行,而不是二进制文件,因为否则所有 \r\n 字节序列将被转换为 \n,从而破坏数据并使文件更短.

此外,在 Windows 的文本模式下,值为 0x1A 的字节被解释为文件结束 (EOF) 标记,即使实际文件长度更长。但是在二进制模式下,具有此值的字节不会被解释为具有任何特殊含义。

std::ifstream file(path);

将以文本模式打开文件,而行

auto f = fopen(path.c_str(), "rb");

将以二进制模式打开文件。

这可能是您获得不同文件长度的原因。