为什么我不能读取超过4094个字符的UTF-16文件?

时间:2017-08-24 20:59:04

标签: c++ linux utf-16 wstring wifstream

一些信息:

  • 我只在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;
    }
  }
}

1 个答案:

答案 0 :(得分:11)

这看起来像是一个库bug。使用gdb

逐步执行gcc 7.1.1编译的示例程序
(gdb) n
28      while (getline(file,line)) {
(gdb) n
29        std::wcout << line << std::endl;
(gdb) p line.size()
$1 = 8000

按预期读取8000个字符。但那时:

(gdb) p line[4092]
$18 = (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628240: 97 L'a'
(gdb) p line[4093]
$19 = (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628244: 98 L'b'
(gdb) p line[4094]
$20 = (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628248: 25344 L'挀'
(gdb) p line[4095]
$21 = (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x62824c: 25600 L'搀'
(gdb) p line[4096]
$22 = (__gnu_cxx::__alloc_traits<std::allocator<wchar_t> >::value_type &) @0x628250: 24832 L'愀'

line[4092]line[4093]看起来不错。但是,我看到line[4094]line[4095]line[4096],包含630064006500,而不是006300640065

所以,这实际上是从角色4094开始搞砸了,而不是4096。我转储了二进制UTF-16文件,看起来对我来说是正确的。 BOM标记之后是文件整个内容的一致字节序。

唯一令人费解的是为什么clang和gcc都会受到影响,但谷歌的快速搜索表明clang也使用gcc的libstdc ++,至少直到最近。所以,这看起来像是一个libstdc ++ bug。