关于字符串:一个Unicode字符占用多少字节?

How many bytes does one Unicode character take?

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

我假设一个Unicode字符可以包含任何语言的每个可能的字符-对吗?那么每个字符需要多少字节呢?

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

我读了维基百科关于Unicode的文章,但这对我来说相当困难。我期待着看到一个简单的答案。


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

1
2
3
4
5
6
Binary    Hex          Comments
0xxxxxxx  0x00..0x7F   Only byte of a 1-byte character encoding
10xxxxxx  0x80..0xBF   Continuation bytes (1-3 continuation bytes)
110xxxxx  0xC0..0xDF   First byte of a 2-byte character encoding
1110xxxx  0xE0..0xEF   First byte of a 3-byte character encoding
11110xxx  0xF0..0xF4   First byte of a 4-byte character encoding

所以快速的答案是:它需要1到4个字节,这取决于第一个字节,它将指示它将占用多少字节。

更新

正如prewett指出的,这条规则只适用于utf-8


你不会看到一个简单的答案,因为没有。

首先,Unicode不包含"每种语言的每一个字符",尽管它确实尝试过。

Unicode本身是一个映射,它定义代码点,代码点是一个数字,通常与字符关联。我说通常是因为有组合字符这样的概念。你可能会熟悉一些东西,比如重音或变音。这些字符可以与另一个字符一起使用,如au来创建新的逻辑字符。因此,一个字符可以由一个或多个代码点组成。

为了在计算系统中有用,我们需要为这些信息选择一种表示。这些是各种各样的Unicode编码,如utf-8、utf-16le、utf-32等,它们的主要区别在于代码单元的大小。UTF-32是最简单的编码,它有一个32位的代码单元,这意味着一个单独的代码点可以很容易地适应一个代码单元。其他编码会有这样的情况:一个代码点需要多个代码单元,或者特定的代码点根本无法在编码中表示(这是UCS-2的一个问题)。

由于组合字符的灵活性,即使在给定的编码中,每个字符的字节数也可能根据字符和规范化形式而变化。这是一种处理具有多个表示形式的字符的协议(您可以说"an 'a' with an accent"是2个码点,其中一个是组合字符,或者"accented 'a'"是一个码点)。


我知道这个问题很老,已经有了一个公认的答案,但我想举几个例子(希望它对某人有用)。

As far as I know old ASCII characters took one byte per character.

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

How many bytes does a Unicode character require?

Unicode只是将字符映射到代码点。它没有定义如何编码它们。文本文件不包含Unicode字符,但可以表示Unicode字符的字节/八位字节。

I assume that one Unicode character can contain every possible
character from any language - am I correct?

不,但差不多。所以基本上是的。但仍然没有。

So how many bytes does it need per character?

和第二个问题一样。

And what do UTF-7, UTF-6, UTF-16 etc mean? Are they some kind Unicode
versions?

不,这些是编码。它们定义字节/八位字节应如何表示Unicode字符。

几个例子。如果浏览器中无法显示其中的某些内容(可能是因为字体不支持),请转到http://codepoints.net/U+1F6AA(用十六进制代码点替换1F6AA)查看图像。

    • U+0061拉丁文小写字母A:a
      • 不:97
      • UTF-8:61
      • UTF 16:00 61
    • U+00A9版权标志:?
      • 不:169
      • UTF-8:C2-A9
      • UTF 16:00 A9
    • U+00AE注册标志:?
      • 不:174
      • UTF-8:C2-AE
      • UTF 16:00 AE
    • U+1337埃塞俄比亚文音节Phwa:?
      • 不:4919
      • UTF-8:E1 8C B7
      • UTF 16:13 37
    • U+2014 Em Dash:
      • 不:8212
      • UTF-8:E2 80 94
      • UTF 16:20 14
    • U+2030/mile标志:
      • 不:8240
      • UTF-8:E2 80 B0
      • UTF 16:20 30
    • U+20AC欧洲标志:
      • 不:8364
      • UTF-8:E2 82 AC
      • UTF 16:20交流
    • U+2122商标标识:?
      • 不:8482
      • UTF-8:E2 84 A2
      • UTF 16:21 22
    • U+2603雪人:?
      • 不:9731
      • UTF-8:E2 98 83
      • UTF 16:26 03
    • U+260E黑色电话:?
      • 不:9742
      • UTF-8:E2 98 8E
      • UTF 16:26 0E
    • 带雨滴的U+2614雨伞:?
      • 不:9748
      • UTF-8:E2 98 94
      • UTF 16:26 14
    • U+263A白色笑脸:?
      • 不:9786
      • UTF-8:E2 98 BA
      • UTF 16:26 3A
    • U+2691黑旗:?
      • 不:9873
      • UTF-8: E2 9A 91
      • UTF 16:26 91
    • U+269B原子符号:?
      • 不:9883
      • UTF-8:E2-9A9B
      • UTF 16:26 9B
    • U+2708飞机:?
      • 不:9992
      • UTF-8:E2 9C 88
      • UTF 16:27 08
    • U+271E带阴影的白色拉丁十字:?
      • 不:10014
      • UTF-8:E2 9C 9E
      • UTF 16:27 1E
    • U+3020邮政标志面:?
      • 不:12320
      • UTF-8:E3 80 A0
      • UTF 16:30 20
    • U+8089 CJK统一汉字-8089:
      • 不:32905
      • UTF-8:E8 82 89
      • UTF 16:80 89
    • 便池U+1F4A9桩:??
      • 不:128169
      • UTF-8:F0 9F 92 A9
      • UTF-16:D8 3D DC A9
    • U+1F680火箭:??
      • 不:128640
      • UTF-8:F0 9F 9A 80
      • UTF-16:D8 3D DE 80
  • 好吧,我快疯了…

    有趣的事实:

    • 如果要查找特定字符,可以将其复制并粘贴到http://codepoints.net/上。
    • 我在这个无用的清单上浪费了很多时间(但它已经排序了!).
    • MySQL有一个称为"utf8"的字符集,它实际上不支持超过3个字节的字符。所以不能插入一堆poo,字段将被自动截断。改为使用"utf8mb4"。
    • 这里有一个雪人测试页面(unicodesnowmanforyou.com)。


    简而言之,Unicode是一种标准,它将一个数字(称为码位)分配给世界上所有的字符(它仍在进行中)。

    现在您需要使用字节来表示这个代码点,称为character encodingUTF-8, UTF-16, UTF-6是表示这些字符的方法。

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

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

    UTF-16对每个字符使用16位,它只表示一部分称为BMP的Unicode字符(就所有实际用途而言,这已经足够了)。Java在字符串中使用此编码。


    在UTF-8中:

    1
    2
    3
    4
    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中:

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

    在UTF 32中:

    1
    4 bytes:      0 - 10FFFF

    根据定义,10ffff是最后一个Unicode码位,它是这样定义的,因为它是UTF-16的技术限制。

    它也是最大的代码点utf-8可以4字节编码,但是utf-8编码背后的思想也适用于5和6字节的编码,以覆盖代码点,直到7fffffff,也就是utf-32可以编码的一半。


    在Unicode中,答案不容易给出。正如你已经指出的,问题在于编码。

    对于任何没有发音字符的英语句子,utf-8的答案将和字符一样多,而对于utf-16,答案将是字符数乘以2。

    唯一可以对大小进行语句的编码是utf-32。在这里,每个字符总是32位,即使我认为代码点已经为未来的UTF-64做好了准备。

    最困难的至少有两件事:

  • 组合字符,用户决定将重音和基础字符(`A)组合在一起,而不是使用已经加重音/音调符号(_)的字符实体。
  • 代码点。代码点是UTF编码所允许的编码方式,编码的位数通常超过了赋予它们名称的位数。例如,utf-8指定了某些字节,这些字节本身是无效的,但当后面跟一个有效的延续字节时,将允许描述超过0..255的8位范围的字符。请参阅下面有关utf-8的维基百科文章中的示例和超长编码。
    • 给出的一个很好的例子是欧元字符(代码点U+20AC可以表示为三字节序列E2 82 AC或四字节序列F0 82 82 AC
    • 这两种方法都是有效的,这说明了当讨论"unicode"而不是特定的unicode编码(如utf-8或utf-16)时,答案有多复杂。

  • 有一个很好的工具可以用utf-8计算任何字符串的字节:http://mothereff.in/byte-counter

    更新:@mathias已将代码公开:https://github.com/mathiasbynens/mothereff.in/blob/master/byte-counter/eff.js


    好吧,我刚刚在维基百科上找到了这个页面,在介绍部分,我看到了"Unicode可以通过不同的字符编码实现"。最常用的编码是utf-8(它对任何ASCII字符使用一个字节,在utf-8和ASCII编码中具有相同的代码值,对其他字符最多使用四个字节),现在已经过时的ucs-2(每个字符使用两个字节,但不能对当前Unicode标准中的每个字符进行编码)。

    正如这句话所表明的,您的问题在于您假设Unicode是一种单一的字符编码方式。实际上有多种形式的Unicode,而且,在该引号中,其中一种甚至每个字符有一个字节,就像您习惯的那样。

    所以你想要的简单答案是它是不同的。


    对于UTF-16,如果字符以0xD800或更大的字符开头,则需要四个字节(两个代码单位);这样的字符称为"代理"


    检查这个Unicode代码转换器。例如,输入0x2009,其中2009是精简空间的Unicode编号,在"0x…"Notation"字段,然后单击Convert。十六进制数E2 80 89【3字节】出现在"UTF-8代码单位"字段中。