为什么 `wifstream` 上的 `getline` 从 UTF-16 编码文件读取乱码输入?
Why does `getline` on `wifstream` read garbled input from UTF-16 encoded file?
在尝试使用 this answer 的提示读取 UTF-16 编码文件时,我遇到了一个问题,在读取几千个字符后,getline
方法开始读取垃圾 mojibake .
这是我的主要内容:
#include <cstdio>
#include <fstream>
#include <iostream>
#include <codecvt>
#include <locale>
int main(void) {
std::wifstream wif("test.txt", std::ios::binary);
setlocale(LC_ALL, "en_US.utf8");
if (wif.is_open())
{
wif.imbue(
std::locale(
wif.getloc(),
new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>
)
);
std::wstring wline;
while (std::getline(wif, wline))
{
std::wcout << wline;
}
wif.close();
}
return 0;
}
test.txt
文件包含FF
、FE
字节顺序标记,后跟100行,每行80'a'
。这是在 *nix:
上生成 test.txt
的 bash 脚本
#!/bin/bash
echo -n -e \xFF\xFE > test.txt
for i in $(seq 1 100)
do
for i in $(seq 1 80)
do
echo -n -e \x61\x00 >> test.txt
done
echo -n -e \x0A\x00 >> test.txt
done
以下是我的编译方式,运行 主要内容:
g++-8 -std=c++17 -g main.cpp -o m && ./m
如我所料: 打印了 8000 'a'
。
实际发生了什么:
打印几千 a
秒后,输出变为以下垃圾:
aaaaaaaaaa愀愀愀愀愀愀愀愀愀愀
偶尔出现不可打印的字符,看起来像 0A00
的矩形。
愀
-字符的二进制代码点值为 110000100000000
,因此它看起来像 a
-字节后跟 0
-字节。
好像在读取的过程中丢失了一些字节,从那以后,一切都错位了,剩下的所有符号都被错误地解码了。或者,因为输出以 0A00
-thingie 结尾,可能是字节序在读取几千 a
s 后颠倒了,但这种行为也没有任何意义。
为什么会发生这种情况,最简单的解决方法是什么?
一个简单的解决方法(但不是通用的解决方案)
如果您确定输入文件具有特定的字节序,那么您可以简单地硬编码字节序 as shown in the example in the documentation:
wif.imbue(
std::locale(
wif.getloc(),
new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>
)
);
使用硬编码 std::little_endian
,问题似乎消失了,文件被正确读取。它可能不适用于具有相反字节序的文件。
在尝试使用 this answer 的提示读取 UTF-16 编码文件时,我遇到了一个问题,在读取几千个字符后,getline
方法开始读取垃圾 mojibake .
这是我的主要内容:
#include <cstdio>
#include <fstream>
#include <iostream>
#include <codecvt>
#include <locale>
int main(void) {
std::wifstream wif("test.txt", std::ios::binary);
setlocale(LC_ALL, "en_US.utf8");
if (wif.is_open())
{
wif.imbue(
std::locale(
wif.getloc(),
new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>
)
);
std::wstring wline;
while (std::getline(wif, wline))
{
std::wcout << wline;
}
wif.close();
}
return 0;
}
test.txt
文件包含FF
、FE
字节顺序标记,后跟100行,每行80'a'
。这是在 *nix:
test.txt
的 bash 脚本
#!/bin/bash
echo -n -e \xFF\xFE > test.txt
for i in $(seq 1 100)
do
for i in $(seq 1 80)
do
echo -n -e \x61\x00 >> test.txt
done
echo -n -e \x0A\x00 >> test.txt
done
以下是我的编译方式,运行 主要内容:
g++-8 -std=c++17 -g main.cpp -o m && ./m
如我所料: 打印了 8000 'a'
。
实际发生了什么:
打印几千 a
秒后,输出变为以下垃圾:
aaaaaaaaaa愀愀愀愀愀愀愀愀愀愀
偶尔出现不可打印的字符,看起来像 0A00
的矩形。
愀
-字符的二进制代码点值为 110000100000000
,因此它看起来像 a
-字节后跟 0
-字节。
好像在读取的过程中丢失了一些字节,从那以后,一切都错位了,剩下的所有符号都被错误地解码了。或者,因为输出以 0A00
-thingie 结尾,可能是字节序在读取几千 a
s 后颠倒了,但这种行为也没有任何意义。
为什么会发生这种情况,最简单的解决方法是什么?
一个简单的解决方法(但不是通用的解决方案)
如果您确定输入文件具有特定的字节序,那么您可以简单地硬编码字节序 as shown in the example in the documentation:
wif.imbue(
std::locale(
wif.getloc(),
new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>
)
);
使用硬编码 std::little_endian
,问题似乎消失了,文件被正确读取。它可能不适用于具有相反字节序的文件。