Подтвердить что ты не робот

Почему я не могу прочитать файл UTF-16 длиной более 4094 символов?

Некоторая информация:

  • Я только пробовал это в Linux
  • Я пробовал как с GCC (7.2.0), так и с Clang (3.8.1)
  • Для моего понимания требуется С++ 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;
    }
  }
}
4b9b3361

Ответ 1

Это выглядит как ошибка библиотеки для меня. Выполнение программы образца, скомпилированной gcc 7.1.1, с помощью gdb:

(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], содержащие 6300, 6400 и 6500 вместо 0063, 0064 и 0065.

Итак, это запутывается, начиная с символа 4094, а не 4096, на самом деле. Я сбросил двоичный файл UTF-16, и он выглядит корректно для меня. За маркой спецификации следует согласованная консистенция для всего содержимого файла.

Единственное, что вызывает недоумение, почему и clang и gcc предположительно затронуты, но быстрый поиск в Google показывает, что clang также использует gcc libstdС++, по крайней мере, до недавнего времени. Итак, это похоже на ошибку libstdС++.