为什么我不能读取超过 4094 个字符的 UTF-16 文件?
Why can I not read a UTF-16 file longer than 4094 characters?
一些资料:
- 我只在 Linux
上试过这个
- 我已经尝试过 GCC (7.2.0) 和 Clang (3.8.1)
- 根据我的理解,它需要 C++11 或更高版本
当我运行它时会发生什么
我得到预期的字符串 "abcd" 重复,直到它到达 4094 个字符的位置。之后它输出的就是这个符号“?”直到文件结束。
我怎么看这个?
我认为这不是预期的行为,它一定是某个地方的错误。
您可以用来测试的代码:
#include <iostream>
#include <fstream>
#include <locale>
#include <codecvt>
void createTestFile() {
std::ofstream file ("utf16le.txt", std::ofstream::binary);
if (file.is_open()) {
uint16_t bom = 0xFEFF; // UTF-16 little endian BOM
uint64_t abcd = 0x0064006300620061; // UTF-16 "abcd" string
file.write((char*)&bom,2);
for (size_t i=0; i<2000; i++) {
file.write((char*)&abcd,8);
}
file.close();
}
}
int main() {
//createTestFile(); // uncomment to make the test file
std::wifstream file;
std::wstring line;
file.open("utf16le.txt");
file.imbue(std::locale(file.getloc(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
if (file.is_open()) {
while (getline(file,line)) {
std::wcout << line << std::endl;
}
}
}
我觉得这像是一个库错误。使用 gdb
:
单步执行由 gcc 7.1.1 编译的示例程序
(gdb) n
28 while (getline(file,line)) {
(gdb) n
29 std::wcout << line << std::endl;
(gdb) p line.size()
= 8000
已读取 8000 个字符,符合预期。但是然后:
(gdb) p line[4092]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628240: 97 L'a'
(gdb) p line[4093]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628244: 98 L'b'
(gdb) p line[4094]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628248: 25344 L'挀'
(gdb) p line[4095]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x62824c: 25600 L'搀'
(gdb) p line[4096]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628250: 24832 L'愀'
line[4092]
和 line[4093]
看起来不错。但是,我看到 line[4094]
、line[4095]
和 line[4096]
,其中包含 6300
、6400
和 6500
,而不是 0063
, 0064
, 和 0065
.
所以,这从字符 4094 而不是 4096 开始变得一团糟。我转储了二进制 UTF-16 文件,它对我来说看起来是正确的。 BOM 标记后跟文件整个内容的一致字节顺序。
唯一令人费解的是为什么 clang 和 gcc 都应该受到影响,但快速 Google 搜索表明 clang 也使用 gcc 的 libstdc++,至少直到最近。所以,这对我来说看起来像是一个 libstdc++ 错误。
一些资料:
- 我只在 Linux 上试过这个
- 我已经尝试过 GCC (7.2.0) 和 Clang (3.8.1)
- 根据我的理解,它需要 C++11 或更高版本
当我运行它时会发生什么
我得到预期的字符串 "abcd" 重复,直到它到达 4094 个字符的位置。之后它输出的就是这个符号“?”直到文件结束。
我怎么看这个?
我认为这不是预期的行为,它一定是某个地方的错误。
您可以用来测试的代码:
#include <iostream>
#include <fstream>
#include <locale>
#include <codecvt>
void createTestFile() {
std::ofstream file ("utf16le.txt", std::ofstream::binary);
if (file.is_open()) {
uint16_t bom = 0xFEFF; // UTF-16 little endian BOM
uint64_t abcd = 0x0064006300620061; // UTF-16 "abcd" string
file.write((char*)&bom,2);
for (size_t i=0; i<2000; i++) {
file.write((char*)&abcd,8);
}
file.close();
}
}
int main() {
//createTestFile(); // uncomment to make the test file
std::wifstream file;
std::wstring line;
file.open("utf16le.txt");
file.imbue(std::locale(file.getloc(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
if (file.is_open()) {
while (getline(file,line)) {
std::wcout << line << std::endl;
}
}
}
我觉得这像是一个库错误。使用 gdb
:
(gdb) n
28 while (getline(file,line)) {
(gdb) n
29 std::wcout << line << std::endl;
(gdb) p line.size()
= 8000
已读取 8000 个字符,符合预期。但是然后:
(gdb) p line[4092]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628240: 97 L'a'
(gdb) p line[4093]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628244: 98 L'b'
(gdb) p line[4094]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628248: 25344 L'挀'
(gdb) p line[4095]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x62824c: 25600 L'搀'
(gdb) p line[4096]
= (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628250: 24832 L'愀'
line[4092]
和 line[4093]
看起来不错。但是,我看到 line[4094]
、line[4095]
和 line[4096]
,其中包含 6300
、6400
和 6500
,而不是 0063
, 0064
, 和 0065
.
所以,这从字符 4094 而不是 4096 开始变得一团糟。我转储了二进制 UTF-16 文件,它对我来说看起来是正确的。 BOM 标记后跟文件整个内容的一致字节顺序。
唯一令人费解的是为什么 clang 和 gcc 都应该受到影响,但快速 Google 搜索表明 clang 也使用 gcc 的 libstdc++,至少直到最近。所以,这对我来说看起来像是一个 libstdc++ 错误。