特定日文Unicode字符串引发C++文件读写异常的原因及解决
问题:wstring读写日文字符时部分字符串被截断
问题背景
长期使用以下C++代码读写包含日文字符的wstring,大部分场景正常运行超一年,但测试特定字符串时出现异常:写入「宴会」后读取仅获首字符,字符串被截断;测试「学校」则完全正常。
读写代码
#include <iostream> #include <sstream> #include <fstream> using namespace std; std::wstring readUnicodeFile(const char* filename) { std::ifstream wif(filename); std::stringstream wss; wss << wif.rdbuf(); std::string const& str = wss.str(); std::wstring wstr; wstr.resize(str.size() / sizeof(wchar_t)); std::memcpy(&wstr[0], str.c_str(), str.size()); // copy data into wstring return wstr; } void writeUnicodetoFile(const char* myFile, wstring& ws) { std::ofstream outFile(myFile, std::ios::out | std::ios::binary); outFile.write((char*)ws.c_str(), ws.length() * sizeof(wchar_t)); outFile.close(); }
测试代码
int main() { wstring test1 = L"学校"; // Japanese word for school wstring test2 = L"宴会"; // Japanese word for banquet writeUnicodetoFile("somefilepath\\test.txt", test1); std::wstring s = readUnicodeFile("somefilepath\\test.txt"); }
异常原因
问题出在读取函数的文本模式打开文件:
- 写入时用了
std::ios::binary二进制模式,正确写入了wchar_t的原始字节(Windows下wchar_t为2字节UTF-16LE编码)。 - 读取时
std::ifstream wif(filename)默认是文本模式,该模式下会把字节0x1A(Windows系统的EOF控制字符)识别为文件结束标志,提前截断读取流。 - 「宴会」的UTF-16编码为
0x5B99 0x4F1A,对应字节序列是0x99 0x5B 0x1A 0x4F——其中0x1A触发了文本模式的截断逻辑,导致后续字节无法读取,最终只得到第一个字符。 - 「学校」的UTF-16编码为
0x5B66 0x6821,对应字节序列是0x66 0x5B 0x21 0x68,不包含文本模式的控制字符,所以读取正常。
修复方案
读取文件时同样使用二进制模式打开,避免文本模式对控制字符的解析:
修改readUnicodeFile函数的文件打开方式:
std::wstring readUnicodeFile(const char* filename) { // 关键:添加std::ios::binary二进制模式 std::ifstream wif(filename, std::ios::binary); std::stringstream wss; wss << wif.rdbuf(); std::string const& str = wss.str(); std::wstring wstr; wstr.resize(str.size() / sizeof(wchar_t)); std::memcpy(&wstr[0], str.c_str(), str.size()); return wstr; }
额外优化建议
- 添加错误处理:检查
wif.is_open()或outFile.is_open()的返回值,避免文件打开失败时的静默错误。 - 跨平台兼容:Windows下
wchar_t是UTF-16,Linux/macOS下是UTF-32,若需跨平台运行,建议统一使用UTF-8编码读写,避免依赖wchar_t的平台差异。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

