如何确定 UTF-16 字符的字节宽度?
How do you determine the byte width of a UTF-16 character?
读取UTF-16字节流,判断一个字符占多少字节的规则是什么?我已经阅读了这些标准,但根据对现实世界 UTF-16 编码流的经验观察,看起来有些地方标准不成立(或者我缺少标准的某个方面) .
来自阅读UTF-16标准https://www.rfc-editor.org/rfc/rfc2781:
Value of leading 2 bytes
Resulting character length (bytes)
0x0000-0xC7FF
2
0xD800-0xDBFF
4
0xDC00-0xDFFF
Invalid sequence (RFC2781 2.2.2)
0xDFFF-0xFFFF
4
在实践中,这似乎是正确的,至少在某些情况下是这样。使用临时 SQL 脚本(SQL Server 2019;UTF-16 整理),但也使用在线解码器进行验证:
Character
Unicode Name
ISO 10646
UTF-16 Encoding (hexadecimal, big endian)
Size (bytes)
A
LATIN CAPITAL LETTER A
U+0041
00 41
2
Б
CYRILLIC CAPITAL LETTER BE
U+0411
04 11
2
ァ
KATAKANA LETTER SMALL A
U+30A1
30 A1
2
RABBIT FACE
U+1F430
D8 3D DC 30
4
然而,当将以下 ISO 10646 字符编码为 UTF-16 时,它似乎是 4 个字节,但读取前 2 个字节似乎没有迹象表明它会这么长:
Character
Unicode Name
UTF-16 Encoding (hexadecimal, big endian)
Size (bytes)
⚕️
STAFF OF AESCULAPIUS
26 95 FE 0F
4
虽然我宁愿让我的问题与软件无关;以下 SQL 将使用默认排序规则和默认语言在 Microsoft SQL Server 2019 上重现此行为。 (注意 SQL 服务器是小端)。
select cast(N'⚕️' as varbinary);
----------
0x95260FFE
很简单,how/why您是否阅读 0x2695
并认为“我需要读下这个字符的下一个词。”?为什么这似乎与已发布的 UTF-16 标准不一致?
你所有的断言都是完全正确的;您对 UTF-16 标准的解释是正确和完整的。
然而,在您的经验观察中,您假设您只有一个角色。实际上,您已经 运行 了解了 Unicode 实现的细微差别。您的“角色”实际上是两个(尽管从技术上讲,不是视觉上的):U+2695“埃斯库拉皮乌斯之杖”,然后是 U+FE0F“VARIATION SELECTOR-16”。 =35=]。第二个字符是 非间距标记 ,它与 基本字符 结合以呈现字符变体。
这会产生字节序列 26 95 FE 0F
,但是正如您所注意到的,这两个词都不属于 UTF-16 保留扩展字符 运行ge。但这是因为它们都不需要 UTF-16 4 字节扩展。它们被简单地归类为两个离散的 Unicode 字符。
来自 7.9 ISO 10646 中的组合标记:通用编码字符集 (UCS):,
Combining marks are a special class of characters in the Unicode Standard that are
intended to combine with a preceding character, called their base.
Combining marks usually have a visible glyphic form... a combining mark may interact graphically with neighbouring characters in various ways.
http://unicode.org/L2/L2010/10038-fcd10646-main.pdf
解释为什么我要回答我自己的问题;我的 SO 问题已准备就绪。我的妻子来到我的办公室;看着我的肩膀后,她在我耳边低声说,“你知道组合字符是一回事,对吧?”。然而,我仍然问了这个问题并自己回答了,以防我妻子的甜言蜜语帮助到社区的另一个成员。
所有这些的正式定义称为“扩展字素簇”,它在 Unicode Text Segmentation report 中定义。正如 Joachim Sauer 指出的那样,谨慎使用 Unicode 中的“字符”一词是明智的。
代码点是“U+....”语法所指的,它试图捕捉书面语言的“单位”,例如“重音符”。但是 reader 会认为一个字符(例如“带有重音符的 e”)是一个“字素簇”,由一个或多个代码点组成。最终呈现在屏幕上的是一个“字形”,它既依赖于上下文又依赖于字体。
Unicode 中的字素簇实际上比这更微妙。 Unicode 试图以一种“中立”的方式来定义它们。 (在考虑语言时确实没有“中性”这样的东西,但 Unicode 确实尝试了。)例如,在斯洛伐克语中,ch、dz 和 dž 都是一个字母,但在 Unicode 中被视为两个字素簇。 (试着数一数斯洛伐克语单词中的“字母”。有些单词包含字母 dz,有些单词包含字母 d 后跟字母 z。哦,人类书写系统。我非常爱你。)
字素簇到字形的映射也很复杂。例如,在阿拉伯语中,单个字形 لا 实际上是两个字素簇,ä(阿拉伯字母 LAM)后跟 ا(阿拉伯字母 ALEF)。如果你用鼠标 select 字形,你会看到有两个 select 可用的部分,如果你将它们复制并粘贴到另一个 window 你会看到它们变成他们的组成部分。 (为了让事情变得更复杂,Unicode 还 为连字定义了一个代码点,阿拉伯连字 LAM WITH ALEF ISOLATED FORM: ﻻ。如果您尝试 select 的一部分那个,你会发现你做不到。这是一个“字符”。)
你的具体情况比较特殊。变体选择器早于 Unicode,主要设计用于处理汉(中文)字符的不同变体。但是,与每个 Unicode 功能一样,它最终主要用于表情符号。 VS-16 是“emoji”的表现形式。最有名的例子就是红心,也就是HEAVY BLACK HEART❤,其次是VS-16:❤️。
同样,你的角色U+2695 STAFF OF AESCULAPIUS是一个单码,默认是这样的(文字样式):⚕。当您添加 VS-16 时,它会以“表情符号样式”呈现:⚕️。在某些方面,它是相同的“字符”。或者是吗?取决于你用它做什么。
表情符号样式通常稍大并居中,有时会添加颜色。请注意在每种情况下绘制五线谱之后的句点(第二个示例中没有多余的空格;字形宽得多)。
还有其他组合系统:
- U+0031: 1
- U+0031 U+20e3: 1⃣ (+ COMBINING ENCLOSING KEYCAP, default text style)
- U+0031 U+20e3 U+fe0f: 1⃣️ (+ VARIATION SELECTOR-16, emoji style)
所有这些都早于 Unicode。 Modern emoji is dramatically more complicated,并包含几个自己的组合系统(包括两个目前仅用于标志)。
但幸运的是,对于您的实际问题,您的妻子是正确的,您通常可以使用所有标记为“组合”的尾随代码点来形成一个扩展的字素簇,这是一种“字符”对于“字符”的一些足够广泛的定义。
读取UTF-16字节流,判断一个字符占多少字节的规则是什么?我已经阅读了这些标准,但根据对现实世界 UTF-16 编码流的经验观察,看起来有些地方标准不成立(或者我缺少标准的某个方面) .
来自阅读UTF-16标准https://www.rfc-editor.org/rfc/rfc2781:
Value of leading 2 bytes | Resulting character length (bytes) |
---|---|
0x0000-0xC7FF |
2 |
0xD800-0xDBFF |
4 |
0xDC00-0xDFFF |
Invalid sequence (RFC2781 2.2.2) |
0xDFFF-0xFFFF |
4 |
在实践中,这似乎是正确的,至少在某些情况下是这样。使用临时 SQL 脚本(SQL Server 2019;UTF-16 整理),但也使用在线解码器进行验证:
Character | Unicode Name | ISO 10646 | UTF-16 Encoding (hexadecimal, big endian) | Size (bytes) |
---|---|---|---|---|
A | LATIN CAPITAL LETTER A | U+0041 | 00 41 |
2 |
Б | CYRILLIC CAPITAL LETTER BE | U+0411 | 04 11 |
2 |
ァ | KATAKANA LETTER SMALL A | U+30A1 | 30 A1 |
2 |
RABBIT FACE | U+1F430 | D8 3D DC 30 |
4 |
然而,当将以下 ISO 10646 字符编码为 UTF-16 时,它似乎是 4 个字节,但读取前 2 个字节似乎没有迹象表明它会这么长:
Character | Unicode Name | UTF-16 Encoding (hexadecimal, big endian) | Size (bytes) |
---|---|---|---|
⚕️ | STAFF OF AESCULAPIUS | 26 95 FE 0F |
4 |
虽然我宁愿让我的问题与软件无关;以下 SQL 将使用默认排序规则和默认语言在 Microsoft SQL Server 2019 上重现此行为。 (注意 SQL 服务器是小端)。
select cast(N'⚕️' as varbinary);
----------
0x95260FFE
很简单,how/why您是否阅读 0x2695
并认为“我需要读下这个字符的下一个词。”?为什么这似乎与已发布的 UTF-16 标准不一致?
你所有的断言都是完全正确的;您对 UTF-16 标准的解释是正确和完整的。
然而,在您的经验观察中,您假设您只有一个角色。实际上,您已经 运行 了解了 Unicode 实现的细微差别。您的“角色”实际上是两个(尽管从技术上讲,不是视觉上的):U+2695“埃斯库拉皮乌斯之杖”,然后是 U+FE0F“VARIATION SELECTOR-16”。 =35=]。第二个字符是 非间距标记 ,它与 基本字符 结合以呈现字符变体。
这会产生字节序列 26 95 FE 0F
,但是正如您所注意到的,这两个词都不属于 UTF-16 保留扩展字符 运行ge。但这是因为它们都不需要 UTF-16 4 字节扩展。它们被简单地归类为两个离散的 Unicode 字符。
来自 7.9 ISO 10646 中的组合标记:通用编码字符集 (UCS):,
Combining marks are a special class of characters in the Unicode Standard that are intended to combine with a preceding character, called their base.
Combining marks usually have a visible glyphic form... a combining mark may interact graphically with neighbouring characters in various ways.
http://unicode.org/L2/L2010/10038-fcd10646-main.pdf
解释为什么我要回答我自己的问题;我的 SO 问题已准备就绪。我的妻子来到我的办公室;看着我的肩膀后,她在我耳边低声说,“你知道组合字符是一回事,对吧?”。然而,我仍然问了这个问题并自己回答了,以防我妻子的甜言蜜语帮助到社区的另一个成员。
所有这些的正式定义称为“扩展字素簇”,它在 Unicode Text Segmentation report 中定义。正如 Joachim Sauer 指出的那样,谨慎使用 Unicode 中的“字符”一词是明智的。
代码点是“U+....”语法所指的,它试图捕捉书面语言的“单位”,例如“重音符”。但是 reader 会认为一个字符(例如“带有重音符的 e”)是一个“字素簇”,由一个或多个代码点组成。最终呈现在屏幕上的是一个“字形”,它既依赖于上下文又依赖于字体。
Unicode 中的字素簇实际上比这更微妙。 Unicode 试图以一种“中立”的方式来定义它们。 (在考虑语言时确实没有“中性”这样的东西,但 Unicode 确实尝试了。)例如,在斯洛伐克语中,ch、dz 和 dž 都是一个字母,但在 Unicode 中被视为两个字素簇。 (试着数一数斯洛伐克语单词中的“字母”。有些单词包含字母 dz,有些单词包含字母 d 后跟字母 z。哦,人类书写系统。我非常爱你。)
字素簇到字形的映射也很复杂。例如,在阿拉伯语中,单个字形 لا 实际上是两个字素簇,ä(阿拉伯字母 LAM)后跟 ا(阿拉伯字母 ALEF)。如果你用鼠标 select 字形,你会看到有两个 select 可用的部分,如果你将它们复制并粘贴到另一个 window 你会看到它们变成他们的组成部分。 (为了让事情变得更复杂,Unicode 还 为连字定义了一个代码点,阿拉伯连字 LAM WITH ALEF ISOLATED FORM: ﻻ。如果您尝试 select 的一部分那个,你会发现你做不到。这是一个“字符”。)
你的具体情况比较特殊。变体选择器早于 Unicode,主要设计用于处理汉(中文)字符的不同变体。但是,与每个 Unicode 功能一样,它最终主要用于表情符号。 VS-16 是“emoji”的表现形式。最有名的例子就是红心,也就是HEAVY BLACK HEART❤,其次是VS-16:❤️。
同样,你的角色U+2695 STAFF OF AESCULAPIUS是一个单码,默认是这样的(文字样式):⚕。当您添加 VS-16 时,它会以“表情符号样式”呈现:⚕️。在某些方面,它是相同的“字符”。或者是吗?取决于你用它做什么。
表情符号样式通常稍大并居中,有时会添加颜色。请注意在每种情况下绘制五线谱之后的句点(第二个示例中没有多余的空格;字形宽得多)。
还有其他组合系统:
- U+0031: 1
- U+0031 U+20e3: 1⃣ (+ COMBINING ENCLOSING KEYCAP, default text style)
- U+0031 U+20e3 U+fe0f: 1⃣️ (+ VARIATION SELECTOR-16, emoji style)
所有这些都早于 Unicode。 Modern emoji is dramatically more complicated,并包含几个自己的组合系统(包括两个目前仅用于标志)。
但幸运的是,对于您的实际问题,您的妻子是正确的,您通常可以使用所有标记为“组合”的尾随代码点来形成一个扩展的字素簇,这是一种“字符”对于“字符”的一些足够广泛的定义。