2

读取 UTF-16 字节流以确定一个字符占用多少字节的规则是什么?我已经阅读了这些标准,但是根据对现实世界 UTF-16 编码流的经验观察,似乎有些地方标准不适用(或者我缺少标准的某个方面) .

从阅读 UTF-16 标准https://www.rfc-editor.org/rfc/rfc2781

前 2 个字节的值 结果字符长度(字节)
0x0000-0xC7FF 2
0xD800-0xDBFF 4
0xDC00-0xDFFF 无效序列 (RFC2781 2.2.2)
0xDFFF-0xFFFF 4

在实践中,这似乎是正确的,至少在某些情况下是这样。使用临时 SQL 脚本(SQL Server 2019;UTF-16 排序规则),但也使用在线解码器进行了验证:

特点 统一码名称 ISO 10646 UTF-16 编码(十六进制,大端) 大小(字节)
一个 拉丁文大写字母 A U+0041 00 41 2
Б 西里尔大写字母 BE U+0411 04 11 2
ァ</td> 片假名字母小 A U+30A1 30 A1 2
兔脸 U+1F430 D8 3D DC 30 4

但是,当将以下 ISO 10646 字符编码为 UTF-16 时,它似乎是 4 个字节,但读取前 2 个字节似乎并没有表明它会这么长:

特点 统一码名称 UTF-16 编码(十六进制,大端) 大小(字节)
⚕️ 埃斯库拉皮乌斯的工作人员 26 95 FE 0F 4

虽然我宁愿让我的问题与软件无关;以下 SQL 将使用默认排序规则和默认语言在 Microsoft SQL Server 2019 上重现此行为。(注意 SQL Server 是小端的)。

select cast(N'⚕️' as varbinary);
----------
0x95260FFE

很简单,您如何/为什么阅读0x2695并认为“我需要阅读这个角色的下一个单词。”?为什么这似乎与已发布的 UTF-16 标准不一致?

4

2 回答 2

3

所有这些的正式定义称为“扩展字素簇”,它在Unicode 文本分割报告中定义。正如 Joachim Sauer 所说,谨慎使用 Unicode 中的“字符”一词是明智的。

代码点是“U+....”语法所指的,并试图捕捉书面语言的“单元”,例如“尖锐的口音”。但是读者会想到一个字符(例如“带有尖锐重音的 e”)是一个“字素簇”,由一个或多个代码点组成。最终呈现到屏幕上的是一个“字形”,它既依赖于上下文,也依赖于字体。

Unicode 中的字素簇实际上比这更微妙。Unicode 试图以“中性”的方式定义它们。(考虑语言时确实没有“中性”之类的东西,但 Unicode 确实尝试过。)例如,在斯洛伐克语中,ch、dz 和 dž 都是一个字母,但在 Unicode 中被视为两个字素簇。(尝试计算斯洛伐克语单词中的“字母”。有些单词包含字母 dz,而其他单词则包含字母 d 后跟字母 z。哦,人类书写系统。我非常爱你。)

字素簇到字形的映射也很复杂。例如,在阿拉伯语中,单个字形 لا 实际上是两个字素簇,ل(阿拉伯字母 LAM)后跟 ا(阿拉伯字母 ALEF)。如果您使用鼠标选择字形,您会看到有两个可选部分,如果您将它们复制并粘贴到另一个窗口,您会看到它们转换为它们的组成部分。(为了让事情变得更复杂,Unicode为连字定义了一个代码点,ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM: ﻻ。如果你尝试选择那个部分,你会发现你不能。它是一个“特点。”)

你的具体情况有点特殊。变体选择器早于 Unicode,主要用于处理不同的汉(中文)字符变体。然而,与每个 Unicode 功能一样,它最终主要用于表情符号。VS-16 是“表情符号”的呈现形式。最著名的例子是红心,也就是 HEAVY BLACK HEART ❤,其次是 VS-16:❤️。

同样,您的角色 U+2695 STAFF OF AESCULAPIUS 是一个单码位,默认情况下(文本样式)如下所示:⚕。添加 VS-16 时,它会以“表情符号样式”呈现:⚕️。在某些方面,它是同一个“角色”。或者是吗?取决于你用它做什么。

表情符号样式通常稍大一些,并在其块中居中,有时会添加颜色。请注意在每种情况下绘制五线谱之后的句点(第二个示例中没有多余的空格;字形要宽得多)。

还有其他组合系统:

  • U+0031: 1
  • U+0031 U+20e3: 1⃣ (+ COMBINING ENCLOSING KEYCAP, 默认文字样式)
  • U+0031 U+20e3 U+fe0f:1⃣️(+ VARIATION SELECTOR-16,表情符号风格)

所有这些都早于 Unicode。现代表情符号要复杂得多,并且包括几个自己的组合系统(包括目前仅用于标志的两个)。

但幸运的是,对于您的实际问题,您的妻子是正确的,您通常可以使用所有标记为“组合”的尾随代码点以形成扩展的字素簇,这对于一些足够广泛的定义来说是一种“字符”的“性格”。

于 2021-04-24T17:38:21.330 回答
1

你所有的断言都是完全正确的;您对 UTF-16 标准的解释是正确和完整的。

然而,在您的经验观察中,您假设您只有一个角色。实际上,您已经遇到了 Unicode 实现的细微差别。您的“角色”实际上是两个(尽管在技术上,而不是视觉上):U+2695 "STAFF OF AESCULAPIUS"后跟U+FE0F "VARIATION SELECTOR-16"。第二个字符是一个非空格标记,它与基本字符组合以呈现字符变体。

这会产生 byte sequence 26 95 FE 0F,但是正如您所注意到的,这两个词都不属于 UTF-16 保留的扩展字符范围。但这是因为它们都不需要 UTF-16 4 字节扩展。它们被简单地归类为两个离散的 Unicode 字符。

ISO 10646中的7.9 组合标记:通用编码字符集 (UCS) :,

组合标记是 Unicode 标准中的一类特殊字符,旨在与前面的字符组合,称为它们的基字符。

组合标记通常具有可见的字形形式……组合标记可以以各种方式与相邻字符进行图形交互。

http://unicode.org/L2/L2010/10038-fcd10646-main.pdf


解释我为什么要回答我自己的问题;我已经准备好提出我的 SO 问题了。我的妻子走进我的办公室;她越过我的肩膀在我耳边低语,“你知道组合字符是一回事,对吧?”。然而,我仍然提出这个问题并自己回答,以防我妻子的甜言蜜语帮助社区的其他成员。

于 2021-04-24T15:40:25.803 回答