为什么 `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文件包含FFFE字节顺序标记,后跟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 结尾,可能是字节序在读取几千 as 后颠倒了,但这种行为也没有任何意义。

为什么会发生这种情况,最简单的解决方法是什么?

一个简单的解决方法(但不是通用的解决方案)

如果您确定输入文件具有特定的字节序,那么您可以简单地硬编码字节序 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,问题似乎消失了,文件被正确读取。它可能不适用于具有相反字节序的文件。