5分钟,搞清 Unicode 与 UTF-8 的区别

Unicode 是什么?

Unicode,官方中文名称“统一码”。通常大家谈到的 Unicode 一般指字符集的意思,但 Unicode 标准实际涵盖两个方面的内容:

  • Unicode 字符集
  • Unicode 编码

Unicode 字符集

Unicode 为每个字符提供一个唯一数字(码点 code point),记为“U+xxxx”,其中xxxx是码位转换成16进制后的结果。

字符->码位的集合即 Unicode 字符集,也称作通用字符集(英语:Universal Character Set, UCS)。可在这里查询字符对应的码位。

例如「中」的码点是 20013,记作 U+4E2D(20013 的十六进制为 0x4E2D)。它的二进制为100111000101101,这至少需要2个字节才能存储到计算机中。那为什么我们看到的是一个个字符,而不是二进制数呢?

Unicode 编码

Unicode 字符集规定了字符对应的唯一码点,但在实际的传输、存储过程中,为了兼顾各系统平台的差异、节省空间,需要对字符集进行编码——将对应的码点转换成另一种格式,即Unicode转换格式(Unicode Transformation Format,简称 UTF)。

Unicode 编码规则决定了码点如何在文件中显示,我们熟知的UTF-8 UTF-16就是编码规则的不同版本。

UTF-8 编码过程

UTF-8 是一种针对 Unicode 的可变长度字符编码,根据码点的大小,将其编码为 1 到 4 个字节,具体规则如下:

U+0000  - U+007F:   0xxxxxxx (1个字节)
U+0080  - U+07FF:   110xxxxx 10xxxxxx (2个字节)
U+0800  - U+FFFF:   1110xxxx 10xxxxxx 10xxxxxx (3个字节)
U+10000 - U+10FFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (4个字节)

「中」的码点为U+4E2D,由上表可知,它属于第三行,被编码成三个字节:

  4   E    2    D
0100 1110 0010 1101
-----------------------------
1110xxxx 10xxxxxx 10xxxxxx  // 上表第三行
____0100 __111000 __101101  // 将 4E2D 的二进制带入上面的格式中
-----------------------------
11100100 10111000 10101101  

以上就是字符「中」经过 UTF-8 编码后得到字节序列的过程,反之亦然。

Unicode 和 UTF-8 的区别

简单来说,Unicode 是【字符集】,UTF-8 是字符集的一种【编码规则】。但广义来讲,它们是包含关系,如下图:

+-----------------------------+
|           Unicode           |
|  +-----------------------+  |
|  |     通用字符集(UCS)     |  |
|  +-----------------------+  |
|  +------------------------+ |
|  |   UCS转换格式(UTF)      |  |
|  | +-------------------+  | |
|  | | UTF-8, UTF-16 ... |  | |
|  | +-------------------+  | |
|  +------------------------+ |
|                             |
+-----------------------------+

补充

字节、字符、字符串

  • 字节:计算机中存储数据的最小单元,一个8bit的二进制数,譬如0x01、0x45...
  • 字符:人类使用的一个个文字符号,在计算机中通常用英文单引号包裹,譬如'中'、'A'、'%'、'。'...
  • 字符串:多个字符 串联起来形成字符串,在计算机中通常用英文双引号包裹,譬如"中华人民共和国万岁"、"MAGA"...

字符以二进制数据形式存储在计算机中,我们在文件中看到的一个个字符,是根据某种编码规则转换后的结果。

字符串长度

同一个字符,在不同的编码规则下,它的长度也不一样。JavaScript 使用 UTF-16 编码,该编码使用 16 比特为1个编码单元来表示大部分常见的字符,使用两个代码单元表示不常用的字符。

'中'.length === 1 // true
'😀'.length === 2 // true

更进一步,我们用 rust 来检验这个规则:

fn main() {
  let s1 = "中";
  assert_eq!(s1.len_utf8(), 3);
  assert_eq!(s1.len_utf16(), 1);
}

最后回答自己一个问题,1个汉字几个字节?

引用

unicode 官网
维基百科-Unicode
维基百科-UCS
字符码点查询

posted @ 2021-10-13 16:56  Liaofy  阅读(1801)  评论(0编辑  收藏  举报