“Ü”的UTF编码返回3个字节​​而不是“真实”unicode

时间:2016-09-20 08:06:14

标签: c++ unicode encoding utf-8

我正在玩下面提到的代码: https://stackoverflow.com/a/21575607/2416394因为我在使用TinyXML编写正确的utf8 xml时遇到了问题。

好吧,我需要编写“带有DIAERESIS的LATIN CAPITAL LETTER U”,Ü可以正确写入XML等。

以下是上述帖子中的代码:

std::string codepage_str = "Ü";
int size = MultiByteToWideChar( CP_ACP, MB_COMPOSITE, codepage_str.c_str(),
                                codepage_str.length(), nullptr, 0 );
std::wstring utf16_str( size, '\0' );
MultiByteToWideChar( CP_ACP, MB_COMPOSITE, codepage_str.c_str(),
                     codepage_str.length(), &utf16_str[ 0 ], size );

int utf8_size = WideCharToMultiByte( CP_UTF8, 0, utf16_str.c_str(),
                                     utf16_str.length(), nullptr, 0,
                                     nullptr, nullptr );
std::string utf8_str( utf8_size, '\0' );
WideCharToMultiByte( CP_UTF8, 0, utf16_str.c_str(),
                     utf16_str.length(), &utf8_str[ 0 ], utf8_size,
                     nullptr, nullptr );

结果是一个std :: string,其大小为3,包含以下字节:

-       utf8_str    "Ü"   std::basic_string<char,std::char_traits<char>,std::allocator<char> >
        [size]  0x0000000000000003  unsigned __int64
        [capacity]  0x000000000000000f  unsigned __int64
        [0] 0x55 'U'    char
        [1] 0xcc 'Ì'    char
        [2] 0x88 'ˆ'    char

当我把它写入utf8文件时。十六进制值保留在那里:0x55 0xCC 0x88和Notepad ++显示正确的字符Ü

然而,当我通过Notepad ++向文件添加另一个Ü并再次保存时,新写的Ü显示为0xC3 0x9C(我实际上预计会在第一时间)。

我不明白,为什么我得到这个字符的3字节表示而不是预期的unicode代码点U + 00DC。

虽然Notepad ++正确显示,但我们的专有系统会将0xC3 0x 9C呈现为Ü,并通过渲染0x55 0xCC 0x88而不会将其识别为两字节utf 8来打破Ü

2 个答案:

答案 0 :(得分:6)

Unicode很复杂。获取Ü至少有两种不同的方式:

  1. LATIN CAPITAL LETTER U WITH DIAERESIS是Unicode代码点U+00DC

  2. LATIN CAPITAL LETTER U是Unicode代码点U+0055COMBINING DIAERESIS是Unicode代码点U+0308

  3. U+00DCU+0055 U+0308都显示为Ü

    在UTF-8中,Unicode代码点U+00DC编码为0xC3 0x9CU+0055编码为0x55U+0308编码为0xCC 0x88 }}

    您的专有系统似乎有一个错误。

    编辑:根据MultiByteToWideChar() documentation获取您的期望,使用MB_PRECOMPOSED代替MB_COMPOSITE

答案 1 :(得分:2)

虽然编码输出在技术上是正确的,但您可以使用NFC表单解决专有系统中的问题。

在NFC形式中,首先分解所有字符(例如,如果U+00DC有代码点Ü,它将被分解为序列U+0055 U+0308)然后重新组合他们的规范表示(在您的示例中,为U+00DC)。

在Win32 API中,请参阅NormalizeString()函数。