Почему `getline` в` wifstream` читает искаженный ввод из файла в кодировке UTF-16? - PullRequest
4 голосов
/ 29 апреля 2019

При попытке прочитать файл в кодировке UTF-16 с подсказками из этого ответа у меня возникла проблема, заключающаяся в том, что после считывания нескольких тысяч символов метод getline начинает читать в мусоджаке для мусора.

Вот мой главный:

#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' с в каждой строке. Вот bash-скрипт, который генерирует test.txt на * nix:

#!/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 s вывод изменится на следующий мусор:

аааааааааа 愀 愀 愀 愀 愀 愀 愀 愀 愀 愀

и иногда непечатные символы, которые выглядят как 0A00 в прямоугольнике.

Символ имеет двоичное значение кодовой точки 110000100000000, поэтому он выглядит как a -байт, а затем 0 -байт.

Кажется, что некоторые байты теряются во время чтения, и с этого момента все смещается, а все остальные символы декодируются неправильно. Или, поскольку выходные данные заканчиваются символом 0A00, может быть, что после считывания нескольких тысяч a s порядок байтов меняется на обратный, но такое поведение также не имеет никакого смысла.

Почему это происходит, и как проще всего это исправить?

1 Ответ

1 голос
/ 29 апреля 2019

Простой обходной путь (но не общее решение)

Если вы уверены, что входной файл будет иметь определенный порядковый номер, тогда вы можете просто жестко закодировать порядковый номер , как показано в примере в документации :

        wif.imbue(
            std::locale(
                wif.getloc(),
                new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>
            )
        );

При жестком коде std::little_endian проблема, похоже, исчезает, и файл читается правильно. Вероятно, он не будет работать для файлов с обратным порядком байтов.

...