为什么将文件读取到 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");
将以二进制模式打开文件。
这可能是您获得不同文件长度的原因。
我已经尝试了各种惯用的 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");
将以二进制模式打开文件。
这可能是您获得不同文件长度的原因。