Windows下MSVC环境中UTF-16文件换行符处理异常问题
UTF-16编码TXT文件分割时换行符异常的解决思路
问题描述
处理UTF-16编码的大文件分割时,出现换行符异常:
- 原UTF-16 LE文件中的换行符
0D 00 0A 00,分割后变为0D 00 0D 0A 00 - 原UTF-16 BE文件中的换行符
00 0D 00 0A,分割后变为00 0D 00 0D 0A
每次写入都会额外添加0D 0A字节。要求仅使用标准库,且采用二进制模式处理。
原始代码
std::wifstream winfile; std::wofstream woutfile; using namespace std; int main() { std::wstring line, temp; unsigned int limit; unsigned int part = 1; //(...) while (!winfile.eof()) { limit = 1000000; woutfile.open(save_path + std::to_string(part) + ".txt", std::ios::out | std::ios::app); limit--; while (limit > 0 && !winfile.eof()) { getline(winfile, temp); wchar_t lf[]{ L'\n'}; woutfile << temp << lf; /* wchar_t lf[]{ 0x00, 0x0D, 0x00, 0x0A}; woutfile << temp << lf; woutfile << temp << L"\n"; woutfile << temp << std::endl; */ limit--; } part++; winfile.close(); woutfile.close(); } system("pause"); }
解决思路
1. 禁用文本模式的自动换行转换
核心问题在于默认的文本模式会自动将换行符在\n和系统原生换行(Windows下为\r\n)之间转换,这会破坏UTF-16编码的字节结构。必须用二进制模式打开文件,彻底禁用这种自动转换:
// 打开输入流时添加二进制标志 winfile.open(input_file_path, std::ios::in | std::ios::binary); // 打开输出流时添加二进制标志 woutfile.open(save_path + std::to_string(part) + ".txt", std::ios::out | std::ios::binary);
2. 正确处理UTF-16的换行符
getline默认会吃掉行尾的\r(文本模式下\r\n被视为单个换行符),导致手动添加\n时,原有的\r加上新增的\n,再叠加文本模式的自动转换,最终出现重复的\r。
二进制模式下,直接写入UTF-16对应的换行符组合L"\r\n"即可,编码后的字节序列就是正确的0D 00 0A 00(LE)或00 0D 00 0A(BE):
// 替换原有的换行写入逻辑 woutfile << temp << L"\r\n";
3. 修复输入流的循环逻辑
原代码中winfile.close()放在外层循环内,第一次循环后就关闭了输入流,后续循环无法读取数据。要将输入流的打开和关闭移到外层循环外:
int main() { std::wstring temp; unsigned int limit; unsigned int part = 1; winfile.open(input_file_path, std::ios::in | std::ios::binary); if (!winfile.is_open()) { // 处理文件打开失败的情况 return 1; } while (!winfile.eof()) { limit = 1000000; woutfile.open(save_path + std::to_string(part) + ".txt", std::ios::out | std::ios::binary); if (!woutfile.is_open()) { // 处理输出文件打开失败的情况 winfile.close(); return 1; } limit--; while (limit > 0 && !winfile.eof()) { getline(winfile, temp); woutfile << temp << L"\r\n"; limit--; } part++; woutfile.close(); } winfile.close(); system("pause"); return 0; }
4. 处理UTF-16的字节序(可选)
如果需要兼容UTF-16 BE和LE,可以通过读取文件开头的BOM(字节顺序标记)判断字节序,再用codecvt facet正确解码:
#include <locale> #include <codecvt> // 读取BOM后设置对应的编码转换 std::wstring_convert<std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>> le_conv; std::wstring_convert<std::codecvt_utf16<wchar_t, 0x10ffff, std::big_endian>> be_conv; // 根据BOM选择对应的转换器处理数据
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

