如何正确地将USC-2小端转换为UTF-8?

时间:2017-04-12 02:21:58

标签: c++ encoding utf-8 c++14

我有一个文件,行结尾是windows样式\r\n;它以USC-2小端编码。

说这是我的文件fruit.txt(USC-2 little endian):

input file

所以我在std::wifstream中打开它并尝试解析内容:

// open the file
    std::wifstream file("fruit.txt");
    if( ! file.is_open() ) throw std::runtime_error(std::strerror(errno));

// create container for the lines
    std::forward_list<std::string> lines;

// Add each line to the container
    std::wstring line;
    while(std::getline(file,line)) lines.emplace_front(wstring_to_string(line));

如果我尝试打印到cout ......

// Printing to cout
    for( auto it = lines.cbegin(); it != lines.cend(); ++it )
        std::cout << *it << std::endl;

......这就是它输出的内容:

Cherry
Banana
ÿþApple

更糟糕的是,如果我在Notepad ++中打开它,它就是它的样子

Null characters everywhere

我可以通过强制将编码转换回USC-2来解决这个问题,从而产生这样的结果:

enter image description here

我的wstring_to_string函数定义如下:

std::string wstring_to_string( const std::wstring& wstr ) {
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
    return convert.to_bytes(wstr);
}

世界上到底发生了什么?如何获得正常的UTF-8字符串?我也试过这种方法:How to read utf-16 file into utf-8 std::string line by line首先导致std::wifstream结果没有输出。有人可以帮助指导我将USC-2 LE数据转换为可读的UTF-8数据吗?

编辑我认为可能存在由MSYS2提供的mingw64 / mingw-w64-x86_64-gcc 6.3.0-2的错误。我已经尝试了所有人的建议,并将区域设置添加到流中只是根本没有输出。我知道只提供了两种本地语言环境,&#34; C&#34;和&#34; POSIX&#34;。我打算尝试Visual Studio,但没有足够的互联网速度进行4GB下载。我使用了像@Andrei R.建议的ICU并且它工作得很好。

我本来喜欢使用标准库,但我对此感到满意。如果您需要此解决方案,请查看我的代码:https://pastebin.com/qudy7yva

3 个答案:

答案 0 :(得分:1)

代码本身很好。

真正的问题是你的输入文件开头是无效的UTF-16LE(你使用std::codecvt_utf8_utf16需要UTF-16,而不是UCS-2)。这在Notepad ++屏幕截图中清晰显示。

另外,文件数据看起来像一个带有BOM的UTF-16LE文件(ÿþ是UTF-16LE BOM,当被视为8位ANSI时)被原样附加到没有BOM的UCS-2BE(或UTF-16BE)文件的结尾。

您需要修复输入文件,以便整个文件从头到尾都是有效的UTF-16LE(前面有或没有BOM,不在中间)。

然后您已经拥有的代码将起作用。

答案 1 :(得分:0)

转换为unicode /从unicode转换通常不是那么简单。看看ICU库,我相信这是迄今为止用于c / c ++的最完整的编码转换库。

还有与WideCharToMultibyte (Win)iconv (Linux)相关的平台相关方式。或者,使用Qt,您可以使用QString::fromUtf16。可能你必须自己反转字节序。

答案 2 :(得分:0)

对于您的情况,主要问题是您使wifstream以错误的方式读取文件。如果你在wstring_to_string中打印wstr的大小,你会发现它不是你所期望的。

https://stackoverflow.com/a/19698449/4005852

设置正确的区域设置将解决此问题。

std::string wstring_to_string( const std::wstring& wstr ) {
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
    return convert.to_bytes(wstr);
}

int main()
{
// open the file
    std::wifstream file("fruit.txt", std::ios::binary);
    file.imbue(std::locale(file.getloc(),
          new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>));
    if( ! file.is_open() ) throw std::runtime_error(std::strerror(errno));

// create container for the lines
    std::forward_list<std::string> lines;

// Add each line to the container
    std::wstring line;
    file.get(); // remove BOM
    while(std::getline(file,line)) lines.emplace_front(wstring_to_string(line));

// Printing to cout
    for( auto it = lines.cbegin(); it != lines.cend(); ++it )
        std::cout << *it << std::endl;

    return 0;
}