【问题标题】:Why is UTF-8 coded on 2 bytes for a U+1xxxx character?为什么 U+1xxxx 字符的 UTF-8 编码为 2 个字节?
【发布时间】:2020-02-17 04:07:24
【问题描述】:

我试图弄清楚 C 如何处理 Unicode 中的字符代码。我将我的语言环境设置为 LC_ALL "fr_CA.UTF8" 然后输入一个带有wscanf() 的字符(作为wchar_t 的数组...)。然后我探索每个 byte 并发现一些奇怪的东西。我输入了 U+1d11e 的高音键(“????”从网页复制)。这实际上是 3 个字节,所以我预计会有 2 个wchar_t。我得到:0x1e、0xd1、0x00、0x00,最后两个是空结尾字符。这是我的代码:

#include <stdio.h>
#include <locale.h>
#include <wchar.h>

int main ( int argc, char* argv[] )
{  
    setlocale( LC_ALL, "fr_CA.utf8" ); 

    wchar_t input[256];

    wscanf( L"%ls", input);
    wprintf( L"%ls\n", input );

    wprintf( L"Length = %d\n", wcslen( input ) );
    wprintf( L"%d\n", (int)(input[0]&0x00ff) );
    wprintf( L"%d\n", (int)((input[0]&0xff00)>>8) );
    wprintf( L"%d\n", (int)(input[1]&0x00ff) );
    wprintf( L"%d\n", (int)((input[1]&0xff00)>>8) );
    wprintf( L"%d\n", (int)(input[2]&0x00ff) );
    wprintf( L"%d\n", (int)((input[2]&0xff00)>>8) );

    return 0;
}  

我的期望是有 0x1e、0xd1、0x01、0x00、0x00、0x00...

但我有 0x1e、0xd1、0x00、0x00...

让我着迷的是wprintf( L"%ls\n", input ); 实际上正确地打印了高音键......那么什么可以区分字符 U+1D11E 和 U+D11E?

另外,我在 Kubuntu 16.04 LTS 的 Konsole 中运行我的程序,并使用 gcc 6.5.0 编译它...如果重要的话。

【问题讨论】:

  • 会不会是您根本没有打印第三个字节? This cleaned up programprintf ???? | a 输出 ???? Length = 1 1D11E
  • 所以首先要做的是确定 wchar_t 的大小。
  • 呃... ????在记事本中保存到 UTF8 给了我字节 F09D849E。 UTF8 是一个扩展系统,因此它的数据之间有控制位,这意味着您无法手动读取这些内容unless you know how,并且它通常最终会比 unicode 符号的值长。通过将上述值转换​​为二进制并去除控制位来解码上述值,得到以下结果:11110[000] 10[011101] 10[000100] 10[011110] => 00001 11010001 00011110 = 1D11E
  • @ikegami 看来你是对的。
  • @Nyerguds 我知道数据位中有控制位。

标签: c unicode utf-8


【解决方案1】:

如果您正确打印了构成 wchar_t 值的字节,或者您只是跳过该字节并打印了它们的值而不试图将它们分解为字节,您会看到您所期望的:

wprintf(L"%x\n", (int)input[0]);
wprintf(L"%x\n", (int)input[1]);

输出将是:

1d11e
0

您尝试执行此操作的方式表明您误以为wchar_t 值是16 位的,并且存在“多wchar_t 字符”之类的东西。 C 语言非常明确地表明没有这样的东西。使用 16 位 wchar_t 的实现是错误(或者至少不能支持 BMP 之外的 Unicode)。当然,一个相当流行的就大错特错了……

我刚刚注意到您在问题的标题中还提到了 UTF-8,但内容与 UTF-8 表示无关。 wchar_t 是(通常;不完全需要)Unicode 代码点编号,相当于 UCS-4(或仅支持 BMP 的实现上的 UCS-2)。虽然几乎可以肯定语言环境的多字节编码必须是 UTF-8 才能让您访问该字符(尽管 GB18030 也可以使用),但如果您正在处理所有数据流,则 UTF-8 不会出现字符流。

【讨论】:

  • Re "不能支持 BMP 之外的 Unicode",除非他们使用代理。例如,JavaScript 就是这样做的。
  • 我猜“一个非常受欢迎的”是来自雷德蒙德的,这就是我不明白的原因。 wchar_t 在 Linux 上是 32 位,在 Windows 上是 16 位。所以,如果我理解正确的话,在 Linux 中用 2 wchar_t 来表示字符串,在 Windows 中用 3 来表示字符串,因为代码点将扩展超过 16 位......?但是在 Linux 中是 64 位,在 Windows 中是 48 位...
  • @ikegami:C 的wchar_t 和相关接口由于它们的工作方式而无法实现。 mbrtowc 无法为多字节字符输出两个 wchar_t 值(这是基本的,不行),isw* 函数无法报告“多-wchar_t-字符”的属性(这可能是某些用户可以接受的限制)。所以不,使用wchar_t 作为 UTF-16 是无法解决的(Windows 尝试这样做,但会破坏标准 C 函数并让您使用它们自己的东西);它确实与语言不兼容。
猜你喜欢
  • 2019-09-30
  • 1970-01-01
  • 1970-01-01
  • 2019-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多