一个Unicode字符占用多少个字节?

296

我对编码有些困惑。据我所知,旧的ASCII字符每个字符占用一个字节。一个Unicode字符需要多少字节呢?

我认为一个Unicode字符可以包含任何语言中的所有可能字符 - 我是正确的吗?那么每个字符需要多少字节呢?

UTF-7、UTF-6、UTF-16等是什么意思?它们是不同版本的Unicode吗?

我看过维基百科关于Unicode的文章,但我觉得很难理解。期待能够得到简单明了的回答。


1
http://en.wikipedia.org/wiki/Comparison_of_Unicode_encodings - Pizzicato
18
抱歉,这个问题没有简单的答案。我觉得整个事情有点混乱。Unicode最初被宣传为使用两个字节并能表示所有字符,但实际上两个字节并不够用。 - Jonathan Wood
14
简单回答:一个 Unicode 字符占用 1 到 4 个字节。Unicode 覆盖了很多语言,但并非全部。例如,克林贡语不是官方的 Unicode 字符集之一。 - Peter G.
10
克林贡语并不属于Unicode标准本身。它使用Unicode的专用使用区(U+F8D0 - U+F8FF)代替。 - Remy Lebeau
2
救星问题 - 谢谢。我的情况是通过符合SCORM 1.2的LMS存储数据... SCORM 1.2的标准'cmi.suspend_data'是4096字节的数据,先前的开发人员认为这意味着我们可以存储4096个字符。哦,天哪,他错了 - 我刚刚发现为什么我们的书签在长课程上失败了。所以现在我知道,由于我们使用UTF-8,每个字符需要4个字节,因此我们有1024个字符。 - danjah
显示剩余3条评论
12个回答

260

令人惊讶的是,没有人指出如何计算一个 Unicode 字符占用多少字节。以下是 UTF-8 编码字符串的规则:

Binary    Hex          Comments
0xxxxxxx  0x00..0x7F   Only byte of a 1-byte character encoding
10xxxxxx  0x80..0xBF   Continuation byte: one of 1-3 bytes following the first
110xxxxx  0xC0..0xDF   First byte of a 2-byte character encoding
1110xxxx  0xE0..0xEF   First byte of a 3-byte character encoding
11110xxx  0xF0..0xF7   First byte of a 4-byte character encoding

所以答案很简单:它需要1到4个字节,具体取决于第一个字节,该字节将指示它将占用多少个字节。


11
我相信一个四字节字符的最大十六进制值是0xF7(而不是0xF4)。 - DJPJ
1
非常感谢!我刚才在 IETF 标准中用 ctrl+f 搜索,没有找到有关编码的任何信息,而且我正在阅读的文章也没有详细说明每个“字符”所需表示的尾代码点数需要使用多少位。 - MarcusJ
1
这现在已经是我“新团队成员介绍”备忘单的第二页,连同前两条幽默的评论。 - Cee McSharpface
3
0xF4 并非错误,而是澄清。Unicode 码位范围为 0-0x10ffff,因此最后一个码位编码为 F4 8F BF BF。 - Frediano Ziglio
1
@DJPJ 你的原则是正确的,但UTF-8并没有使用所有可用的空间,以确保与UTF-16的兼容性。 - Chris - Regenerate Response

180

你想要得到的答案并不简单,因为它并不存在。

首先,Unicode 并不包含“每一种语言的每一个字符”,尽管它确实在努力尝试。

Unicode 本身是一种映射,它定义了码点,而码点是一个数字,通常与一个字符关联。我说“通常”是因为有像组合字符这样的概念。你可能熟悉像重音或分音符这样的东西。它们可以与另一个字符(如 au)一起使用,从而创建一个新的逻辑字符。因此,一个字符可以由一个或多个码点组成。

为了在计算机系统中变得有用,我们需要选择一种表示这些信息的方式。这些就是各种 Unicode 编码,例如 utf-8、utf-16le、utf-32 等。它们主要通过其代码单元的大小来区分。UTF-32 是最简单的编码,它具有一个 32 位的代码单元,这意味着一个单独的码点可以轻松地放入代码单元中。其他编码将出现需要多个代码单元才能表示一个码点的情况,或者该特定码点根本无法在编码中表示(这是 UCS-2 中的问题)。

由于组合字符的灵活性,即使在给定的编码中,每个字符所占用的字节数也会因字符和规范化形式而异。这是处理具有多个表示形式的字符的一种协议(你可以说“带有重音符号的 ‘a’”,它由两个码点组成,其中一个是一个组合字符,或者 “带重音符号的‘a’”,它只有一个码点)。


1
好的。那么,一个给定的字符在一个给定的代码点中表示需要多少字节呢?例如,非断空格。 - Nicolas Barbulesco
1
当程序员在UTF8数组上编写strlen()、substr()和其他字符串操作函数时,组合字符会让他们的生活变得非常困难。这种工作永远不会完成,而且总是有漏洞的。 - Nulik
我编写了一个演示程序,展示了使用Windows-1252、UTF8和UTF8-BOM编码的文件在每种编码下的解释,并比较了结果之间的相等性:https://github.com/vladyrn/encodings_demo - Vlad Nestorov
实际上,任何西方语言都不会使用两个代码点来表示一个字符。对于CJK(中日韩)语言我不太确定,但是组合字符只有在表示星猫表情符号时西方人才会看到,因为所有使用变音符号的字符也都存在独立的代码点。 - undefined

54

虽然我知道这个问题很久了,并且已经有一个被接受的答案,但是我想提供一些例子(希望对某些人有用)。

据我所知,旧的ASCII字符每个字符占用一个字节。

没错。实际上,由于ASCII是7位编码,它支持128个代码(其中95个可打印),因此它只使用半个字节(如果这有任何意义的话)。

一个Unicode字符需要多少字节?

Unicode只是将字符映射到码点。它不定义如何对它们进行编码。文本文件中不包含Unicode字符,而是可能表示Unicode字符的字节/八位组。

我认为一个Unicode字符可以包含任何语言的所有可能字符 - 我是正确的吗?

不完全正确。但几乎是的。所以基本上是的。但仍然不完全正确。

那么每个字符需要多少字节?

与您的第二个问题相同。

UTF-7、UTF-6、UTF-16等是什么?它们是一些Unicode版本吗?

不,它们是编码。它们定义了如何使用字节/八位组表示Unicode字符。

以下是一些例子。如果有些例子无法在您的浏览器中显示(可能是因为字体不支持),请转到 http://codepoints.net/U+1F6AA (将1F6AA替换为十六进制代码点)以查看图像。

    • U+0061 LATIN SMALL LETTER A: a
      • Nº: 97
      • UTF-8: 61
      • UTF-16: 00 61
    • U+00A9 COPYRIGHT SIGN: ©
      • Nº: 169
      • UTF-8: C2 A9
      • UTF-16: 00 A9
    • U+00AE REGISTERED SIGN: ®
      • Nº: 174
      • UTF-8: C2 AE
      • UTF-16: 00 AE
    • U+1337 ETHIOPIC SYLLABLE PHWA:
      • Nº: 4919
      • UTF-8: E1 8C B7
      • UTF-16: 13 37
    • U+2014 EM DASH:
      • Nº: 8212
      • UTF-8: E2 80 94
      • UTF-16: 20 14
    • U+2030 PER MILLE SIGN:
      • Nº: 8240
      • UTF-8: E2 80 B0
      • UTF-16: 20

        好的,我太沉迷于这个话题了...

        有趣的事实:


有什么区别吗?BE不是代表大端序吗?他用大端序写的,因此用大端序UTF-16编写的文件与UTF-16BE相同,对吗? - Grifball
10
更正:1)ASCII码使用7位,而一个字节有8位,因此它比半数还多。2)Unicode规定了如何对代码点进行编码。UTF-8、UTF-16和UTF-32是在Unicode标准中定义的。 - Jonathan Rosenne
7
我认为他/她的意思是,它只使用了8位可表示的可能值的一半,而不是只使用了一半的位数。 - Aritz Lopez
4
我很喜欢这些例子。它们突显了为什么有人可能更喜欢UTF-16而不是UTF-8。不同软件开发人员可能会根据哪些Unicode字符更有可能使用来选择不同的编码方式。例如,在中国/日本,UTF-16(2字节)比UTF-8更合适,因为相同的字符在UTF-8中通常需要两倍的字节数来编码。 - mike
上次我检查的时候,⅞不等于½。 - undefined
显示剩余2条评论

36

简单来说,Unicode 是一个标准,它为世界上的所有字符(它仍在不断完善中)分配了一个数字(称为码点)。

现在您需要使用字节来表示这些码点,这就是所谓的字符编码UTF-8,UTF-16,UTF-6 是表示这些字符的方式。

UTF-8 是一种多字节字符编码。字符可以有1到6个字节(其中一些可能现在不需要)。

UTF-32每个字符都有4个字节。

UTF-16 每个字符使用16位,并且仅表示 Unicode 字符的一部分,称为 BMP (对于所有实际目的而言已足够)。Java 在其字符串中使用此编码。


12
Unicode是一个21位的编码集,使用4个字节就足以在UTF-8中表示任何Unicode字符。UTF-16使用代理项来表示BMP(基本多语言平面)之外的字符;它需要2个或4个字节来表示任何有效的Unicode字符。UCS-2是仅支持16位的UTF-16变体,不支持代理项或BMP之外的字符。 - Jonathan Leffler
1
你是正确的。UTF-8原始编码有6个字节来容纳32位。我实际上不想让事情变得更复杂,因为他已经被维基百科的文档搞糊涂了 :) - Zimbabao
3
这个答案声称UTF-16不能编码BMP码位,这是不正确的。因为就像UTF-8一样,可以使用代理对来编码它们。(你可能正在考虑已过时的UCS-2,在Unicode 2.0之前只编码16位码位。)另外,Java并没有完全使用UTF-16,而是使用了一种修改形式,其中码位0被以不同的方式编码。 - rdb
@rdb - 恰恰相反。答案说UTF-16表示BMP。 - Nicolas Barbulesco
3
我打错了,我本来想说“非BMP字符”。答案中的错误在于它说UTF-16表示BMP字符,这是不准确的。UTF-16可以编码所有Unicode字符,而非BMP字符则通过代理对进行编码。也许答题者混淆了UCS-2。 - rdb

23
在Unicode中,每个字符都用从零到0x10FFFF的整数表示。以32位整数的方式进行编码被称为UTF-32编码。为了更节省空间,UTF-8和UTF-16是需要更少空间来表示较低码点的编码方式。
需要注意的是,在实现中所称的UTF-16通常实际上只是UCS2:UTF-16可以适应32位的码点子集。
存储要求如下。
在UTF-8中:
1 byte:       0 -     7F  (ASCII)
2 bytes:     80 -    7FF  (all European plus some Middle Eastern)
3 bytes:    800 -   FFFF  (multilingual plane incl. the top 1792 and private-use)
4 bytes:  10000 - 10FFFF

在UTF-16中:
2 bytes:      0 -   D7FF  (multilingual plane except the top 1792 and private-use)
4 bytes:   D800 - 10FFFF

在UTF-32中:
4 bytes:      0 - 10FFFF

10FFFF是根据定义的最后一个Unicode代码点,之所以定义成这样,是因为这是UTF-16的技术限制。
它也是UTF-8可以用4个字节编码的最大代码点,但UTF-8编码背后的思想也适用于5字节和6字节编码,以覆盖代码点直到7FFFFFFF,即UTF-32的一半。

8
在Unicode中,这个问题并不容易回答。正如您已经指出的那样,问题在于编码。
对于任何没有变音符号的英语句子,UTF-8的答案是字符数量的字节数,而UTF-16的答案是字符数量乘以2。
目前唯一一个我们可以关于大小做出陈述的编码方式是UTF-32。在那里,每个字符始终占用32位,尽管我想象代码点为未来的UTF-64做好了准备 :)
使其如此困难的至少有两个原因:
  1. 组合字符使用方法是将重音符号和基础字符相结合 (`A),而不是使用已经带有重音符号或变音符号的字符实体 (À)。
  2. 码点是UTF编码允许编码超出命名所使用的比特数范围的方法。例如,UTF-8指定某些字节,这些字节本身无效,但当后跟一个有效的续字符时,可以描述一个超出0..255 8位范围的字符。在维基百科关于UTF-8的文章中,参见ExamplesOverlong Encodings
    • 其中提供的优秀示例是欧元符号(码点U+20AC)可以表示为三字节序列E2 82 AC四字节序列F0 82 82 AC
    • 两种表示法都是有效的,这表明当谈论“Unicode”而不是Unicode的特定编码方式(如UTF-8或UTF-16)时,答案会变得很复杂。严格来说,正如评论中指出的那样,这似乎不再是事实,甚至可能基于我个人的误解。来自更新的维基百科文章的引述如下更长的编码称为过长编码,不是代码点的有效UTF-8表示

关于“过长”的UTF-8示例(4个字节而不是3个字节),您写道“...它们都是有效的...”,但链接的维基百科文章不再同意;<quote>“更长的编码称为过长,并且不是代码点的有效UTF-8表示。此规则保持代码点和其有效编码之间的一对一对应,以便每个代码点都有唯一的有效编码。”<end-quote> - Glenn Slayden
@GlennSlayden 谢谢您发现并通知我。我希望现在已经纠正了这个问题。与其传播错误/误导性信息,我宁愿删除我赞过的答案。 - 0xC0000022L

8

5
我刚刚也在维基百科上查到了相关页面,在简介部分看到了这句话:“Unicode可以通过不同的字符编码实现。最常用的编码是UTF-8(对于任何ASCII字符都使用一个字节,这些字符在UTF-8和ASCII编码中具有相同的代码值,并且为其他字符使用多达四个字节),现已过时的UCS-2(每个字符使用两个字节,但无法编码当前Unicode标准中的所有字符)。”正如这句话所示,你的问题在于你认为Unicode是一种编码字符的单一方式。实际上有多种形式的Unicode,而且就像那句话所说,其中一种甚至每个字符都只有1个字节,��像你习惯的那样。所以,你想要的简单答案是它是变化的。

4

Unicode是一种标准,为每个字符提供唯一的编号。这些唯一的编号被称为码点,适用于世界上存在的所有字符(有些还需要添加)。

出于不同的目的,您可能需要以字节表示这些码点(大多数编程语言都这样做),这就是字符编码的作用。

UTF-8UTF-16UTF-32等都是字符编码,Unicode的码点以不同的方式在这些编码中表示。


UTF-8编码具有可变宽度长度,并且以其编码的字符可以占用1到4个字节。

UTF-16具有可变长度,其中编码的字符可以使用1或2个字节(即8或16位)。这仅代表称为BMP(基本多语言平面)的所有Unicode字符的一部分,并且几乎适用于所有情况。Java使用UTF-16编码其字符串和字符;
UTF-32具有固定长度,每个字符占用4个字节(32位)。

3

对于UTF-16编码,如果字符以0xD800或更大的值开头,则需要四个字节(即两个代码单元)来表示该字符;这种字符被称为“代理对”(surrogate pair)。具体来说,代理对的格式如下:

[0xD800 - 0xDBFF]  [0xDC00 - 0xDFF]

其中[...]表示具有给定范围的两个字节代码单元。任何小于等于0xD7FF的值都是一个代码单元(两个字节)。任何大于等于0xE000的值都是无效的(除了BOM标记,可以说)。

请参见http://unicodebook.readthedocs.io/unicode_encodings.html,第7.5节。


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