UTF-8编码如何识别单字节和双字节字符?

37

最近我在处理字符编码时遇到了一个问题,当我深入研究字符集和字符编码时,我产生了这个疑问。UTF-8编码是最流行的编码之一,因为它与ASCII向后兼容。由于UTF-8是可变长度编码格式,它如何区分单字节和双字节字符。例如,"Aݔ" 存储为 "410754"(A的Unicode为41,阿拉伯字符的Unicode为0754)。编码如何确定41是一个字符,而0754是另一个双字节字符?为什么不将它视为一个双字节字符4107和一个单字节字符54呢?


3
请注意,即使在英语中,许多“外来”的字符也经常被使用,因此“非英语”一词意义不大。如果你认为这不是事实,那么你就有点天真了。请注意括号中的符号,这是表示傻笑的表情符号。 - deceze
在UTF-8中,没有“双字节”字符的强烈概念。UTF-8将每个Unicode代码点编码为一个到四个代码单元。对于二进制和三进制之间没有特别之处。 - Tom Blodget
实际上,“Aݔ”存储为“00410754”,即2个16位字符。如下所示,字符串UTF-8编码为“41DD94”。您的帖子有点令人困惑,因为它可能被理解为UTF-8是“410754”。 - crass
如果您想确定特定字符需要多少字节,可以使用以下代码:var byteCount = Encoding.UTF8.GetByteCount(new char[] {ch}); - Eric J.
以上是 C# 代码。这里提供 JavaScript 代码,如果有人需要: (new TextEncoder().encode("ݔ")).length - Anmol Saraf
3个回答

69
例如,“Aݔ”被存储为“410754”。 这不是UTF-8的工作方式。 字符U+0000到U+007F(也称为ASCII)存储为单个字节。它们是唯一一个其代码点数值与其UTF-8表示相符的字符。例如,U+0041变成0x41,即二进制中的01000001。 所有其他字符都用多个字节表示。U+0080到U+07FF每个使用两个字节,U+0800到U+FFFF每个使用三个字节,U+10000到U+10FFFF每个使用四个字节。 计算机可以知道一个字符在哪里结束,下一个字符从哪里开始,因为UTF-8是这样设计的,使得用于ASCII的单字节值不重叠于多字节序列中使用的值。字节0x000x7F仅用于ASCII,而以上的字节仅用于多字节序列。此外,在多字节序列开头使用的字节也不能出现在序列的任何其他位置。 因此需要对代码点进行编码。请考虑以下二进制模式:
  • 2字节:110xxxxx 10xxxxxx
  • 3字节:1110xxxx 10xxxxxx 10xxxxxx
  • 4字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
第一个字节中的1的数量告诉你后面还有多少个字节属于同一个字符。所有属于该序列的字节以二进制形式10开头。要对字符进行编码,请将其代码点转换为二进制,并填写x的位置。例如:U+0754位于U+0080和U+07FF之间,因此需要两个字节。0x0754的二进制形式为11101010100,因此您将x替换为这些数字:

11011101 10010100


现在我明白了UTF-8编码的工作原理以及它如何从字节序列中识别单字节和双字节字符。因此,UTF-8遵循这种模式来识别单个字符的连续字节,那么其他编码格式也有这种模式来分离单个字符的字节,对吗? - Ganesh kumar S R
5
另外两种Unicode格式是UTF-16和UTF-32。UTF-32每个单位使用四个字节,足以存储所有可能的Unicode值,因此所有代码点都被简单地保存未经修改。 U+0754变成00 00 07 54。UTF-16每个单位使用两个字节,对于U+FFFF及以下的所有字符足够,因此U+0754变成07 54。超过FFFF的所有内容都使用两个所谓的代理对进行编码,这是特殊的两个字节代码点,不由任何字符使用。同样,起始代理和结束代理的代码点不重叠。 - CharlotteBuff
@RandomGuy32 感谢您的回答。我也在努力理解这些东西。“U+0754在U+0080和U+07FF之间”,我不太明白这个数字系统。你怎么知道它在中间?此外,“0x0754”是否被认为是十六进制格式?我不明白你是如何将其转换为二进制格式的。谢谢。 - Moondra
@Moondra 是的,Unicode代码点最常用的是十六进制表示,因此U+0754只是表示十六进制数0754,它在十进制中的值为1876。 - CharlotteBuff
很好的回答,有一个澄清,当你说ASCII时,我认为你指的是7位ASCII。 - kevinc
显示剩余2条评论

28

简短回答:

UTF-8旨在能够明确地识别文本流中每个字节的类型:

  • 1字节代码(所有且仅限于ASCII字符)以0开头
  • 2字节代码的前导字节以两个1和一个0开头(即110
  • 3字节代码的前导字节以三个1和一个0开头(即1110
  • 4字节代码的前导字节以四个1和一个0开头(即11110
  • 所有多字节代码的连续字节以一个1和一个0开头(即10

你的例子 由Unicode代码点U+0041和U+0754组成,它在UTF-8中的编码为:

01000001 11011101 10010100

因此,在解码时,UTF-8知道第一个字节必须是一个1字节代码,第二个字节必须是2字节代码的前导字节,第三个字节必须是一个连续字节,由于第二个字节是2字节代码的前导字节,所以第二个和第三个字节必须组成这个2字节代码。


请参见此处了解UTF-8如何编码Unicode代码点。


1

需要澄清的是,ASCII意味着标准的7位ASCII,而不是欧洲常用的扩展8位ASCII。

因此,第一个字节的一部分(0x80到0xFF)采用双字节表示,第二个字节的一部分(0x0800到0xFFFF)采用完整的三字节表示。

四字节表示仅使用最低的三个字节,并且仅使用了‭16,777,215中的1,114,111种可能性。

你可以在这里找到xls 链接

这意味着解释器必须在发现这些二进制模式时“跳回”NUL(0)字节。

希望这能帮助有人!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接