乐趣区

关于unicode:详解字符编码与-Unicode

人类交换应用 ABC 等字符,但计算机只意识 01。因而,就须要将人类的字符,转换成计算机意识的二进制编码。这个过程就是字符编码。

ASCII

最简略、罕用的字符编码就是 ASCII(American Standard Code for Information Interchange,美国信息替换规范代码),它将美国人最罕用的 26 个英文字符的大小写和罕用的标点符号,编码成 0127 的数字。例如 A 映射成 65 (0x41),这样计算机中就能够用 0100 0001 这组二进制数据,来示意字母 A 了。

ASCII 编码的字符能够分成两类:

  • 控制字符:031127 (0x000x1F0x7F)
  • 可显示字符:32126 (0x200x7E)

具体字符表能够参考:ASCII – 维基百科,自在的百科全书。

Unicode

ASCII 只编码了美国罕用的 128 个字符。显然不足以满足世界上这么多国家、这么多语言的字符应用。于是各个国家和地区,就都开始对本人须要的字符设计其余编码方案。例如,中国有本人的 GB2312,不够用了之后又扩大了 GBK,还是不够用,又有了 GB18030。欧洲有一系列的 ISO-8859 编码。这样各国人民就都能够在计算机上解决本人的语言文字了。

但每种编码方案,都只思考了本人用到的字符,没方法跨服交换。如果一篇文档里,同时应用了多种语言的字符,总不能别离指定哪个字符应用了那种编码方式。

如果能对立给世界上的所有字符调配编码,就能够解决跨服交换的问题了,Unicode 就是来干这个事件的。

Unicode 对立编码了世界上大部分的字符,例如将 A 编码成 0x00A1,将 编码成 0x4E2D,将 α 编码成 0x03B1。这样,中国人、美国人、欧洲人,就能够应用同一种编码方式交换了。

一个 Unicode 字符能够应用 U+ 和 4 到 6 个十六进制数字来示意。例如 U+0041 示意字符 AU+4E2D 示意字符 U+03B1 示意字符 α

Unicode 最后编码的范畴是 0x00000xFFFF,也就是两个字节,最多 65536 (2^16) 个字符。但随着编码的字符越来越多,两个字节的编码空间曾经不够用,因而又引入了 16 个辅助立体,每个辅助立体同样最多蕴含 65536 个字符。原来的编码范畴称为根本立体,也叫第 0 立体。

各立体的字符范畴和名称如下表:

立体 字符范畴 名称
0 号立体 U+0000U+FFFF 根本多文种立体 (Basic Multilingual Plane, BMP)
1 号立体 U+10000U+1FFFF 多文种补充立体 (Supplementary Multilingual Plane, SMP)
2 号立体 U+20000U+2FFFF 表意文字补充立体 (Supplementary Ideographic Plane, SIP)
3 号立体 U+30000U+3FFFF 表意文字第三立体 (Tertiary Ideographic Plane, TIP)
14 号立体 U+E0000U+EFFFF 特地用处补充立体
15 号立体 U+F0000U+FFFFF 保留作为私人应用区(A 区)(Private Use Area-A, PUA-A)
16 号立体 U+100000U+10FFFF 保留作为私人应用区(B 区)(Private Use Area-B, PUA-B)

每个立体内还会进一步划分成不同的区段。每个立体和区段具体阐明参考 Unicode 字符立体映射 – 维基百科,自在的百科全书;汉字相干的区段阐明参考 中日韩对立表意文字 – 维基百科,自在的百科全书。Unicode 所有字符按立体和区段查找,能够参考 Roadmaps to Unicode;按区域和语言查找能够参考 Unicode Character Code Charts。

字符编码的基本概念

“字符编码”是一个含糊、抽象的概念,为了进一步阐明字符编码的过程,须要将其拆解为一些更加明确的概念:

字符 (Character)

人类应用的字符。例如:

  • A
  • 等。

编码字符集 (Coded Character Set, CCS)

把一些字符的汇合 (Character Set) 中的每个字符 (Character),映射成一个编号或坐标。例如:

  • 在 ASCII 中,把 A 编号为 65 (0x41);
  • 在 Unicode 中,把 编号为 0x4E2D
  • 在 GB2312 中,把 映射到第 54 区第 0 位。

这个映射的编号或坐标,叫做 Code Point。

Unicode 就是一个 CCS。

字符编码表 (Character Encoding Form, CEF)

把 Code Point 转换成特定长度的整型值的序列。这个特定长度的整型值叫做 Code Unit。例如:

  • 在 ASCII 中,0x41 这个 Code Point 会被转换成 0x41 这个 Code Unit;
  • 在 UTF-8 中,0x4E2D 这个 Code Point 会被转换成 0xE4 B8 AD 这三个 Code Unit 的序列。

咱们罕用的 UTF-8、UTF-16 等,就是 CEF。

字符编码方案 (Character Encoding Scheme, CES)

把 Code Unit 序列转换成字节序列(也就是最终编码后的二进制数据,供计算机应用)。例如:

  • 0x0041 这个 Code Unit,应用大端序会转换成 0x00 41 两个字节;
  • 应用小端序会转换成 0x41 00 两个字节。

UTF-16 BE、UTF-32 LE 等,就是 CES。


这些概念间的关系如下:

@startuml

hide empty description

state Character
state CodePoint
state CodeUnits
state Bytes

Character : A
CodePoint : 0x41
CodeUnits : 0x0041
Bytes : 0x41 0x00

Character -right-> CodePoint : CCS (Unicode)
CodePoint -right-> CodeUnits : CEF (UTF-16)
CodeUnits -right-> Bytes : CES (UTF-16 LE)

@enduml

因而,咱们说 ASCII 是“字符编码”时,“字符编码”指的是下面从 Character 到字节数组的整个过程。因为 ASCII 足够简略,两头的 Code Point 到 Code Unit,再到字节数组,都是一样的,没必要拆开说。

而咱们说 Unicode 是“字符编码”时,“字符编码”其实指的仅是下面的 CCS 局部。

同理,ASCII、Unicode、UTF-8、UTF-16、UTF-16 LE,都能够抽象的叫做“字符编码”,但每个“字符编码”示意的含意都是不同的。可能是 CCS、CEF、CES,也可能是整个过程。

Unicode 转换格局

Unicode 只是把字符映射成了 Code Point (字符编码表,CCS)。将 Code Point 转换成 Code Unit 序列(字符编码表,CEF),再最终将 Code Unit 序列转换成字节序列(字符编码方案,CES),有多种不同的实现形式。这些实现形式叫做 Unicode 转换格局 (Unicode Transformation Format, UTF)。次要包含:

  • UTF-32
  • UTF-16
  • UTF-8

UTF-32

UTF-32 将每个 Unicode Code Point 转换成 1 个 32 位长的 Code Unit。

UTF-32 是固定长度的编码方案,每个 Code Unit 的值就是其 Code Point 的值。例如 0x00 00 00 41 这个 Code Unit,就示意了 0x0041 这个 Code Point。

UTF-32 的一个 Code Unit,须要转换成 4 个字节的序列。因而,有大端序 (UTF-32 BE) 和小端序 (UTF-32 LE) 两种转换形式。

例如 0x00 00 00 41 这个 Code Unit,应用 UTF-32 BE 最终会编码为 0x00 00 00 41;应用 UTF-32 LE 最终会编码为 0x41 00 00 00

UTF-16

UTF-16 将每个 Unicode Code Point 转换成 1 到 2 个 16 位长的 Code Unit。

对于根本立体的 Code Point(0x00000xFFFF),每个 Code Point 转换成 1 个 Code Unit,Code Unit 的值就是其对应 Code Point 的值。例如 0x0041 这个 Code Unit,就示意了 0x0041 这个 Code Point。

对于辅助立体的 Code Point(0x0100000x10FFFF),每个 Code Point 转换成 2 个 Code Unit 的序列。如果还是间接应用 Code Point 数值转换成 Code Unit,就有可能和根本立体的编码重叠。例如 U+010041 如果转换成 0x00010x0041 这两个 Code Unit,解码的时候没方法晓得这是 U+010041 一个字符,还是 U+0001U+0041 两个字符。

为了让辅助立体编码的两个 Code Unit,都不与根本立体编码的 Code Unit 重叠,就须要利用根本立体中一个非凡的区段了。根本立体中规定了从 0xD8000xDFFF 之间的区段,是永恒保留不映射任何字符的。UTF-16 将辅助立体的 Code Point,编码成一对在这个范畴内的 Code Unit,叫做代理对。这样解码的时候,如果解析到某个 Code Unit 在 0xD8000xDFFF 范畴内,就晓得他不是根本立体的 Code Unit,而是要两个 Code Unit 组合在一起去示意 Code Point。

具体转换形式是:

  1. 将辅助立体的 Code Point 的值 (0x0100000x10FFFF),减去 0x010000,失去 0x000000xFFFFF 范畴内的一个数值,也就是最多 20 个比特位的数值
  2. 将前 10 位的值(范畴在 0x00000x03FF),加上 0xD800,失去范畴在 0xD8000xDBFF 的一个值,作为第一个 Code Unit,称作高位代理或前导代理
  3. 将后 10 位的值(范畴在 0x00000x03FF),加上 0xDC00,失去范畴在 0xDC000xDFFF 的一个只,作为第二个 Code Unit,称作低位代理或后尾代理

根本立体中的 0xD8000xDBFF0xDC000xDFFF 这两个区段,也别离叫做 UTF-16 高半区 (High-half zone of UTF-16) 和 UTF-16 低半区 (Low-half zone of UTF-16)。

UTF-16 的一个 Code Unit,须要转换成 2 个字节的序列。因而,有大端序 (UTF-16 BE) 和小端序 (UTF-16 LE) 两种转换形式。

例如 0x0041 这个 Code Unit,应用 UTF-16 BE 最终会编码为 0x0041;应用 UTF-16 LE 最终会编码为 0x4100

UTF-8

UTF-8 将每个 Unicode Code Point 转换成 1 到 4 个 8 位长的 Code Unit。

UTF-8 是不定长的编码方案,应用前缀来标识 Code Unit 序列的长度。解码时,依据前缀,就晓得该将哪几个 Code Unit 组合在一起解析成一个 Code Point 了。

具体编码方式是:

Code Point 范畴 Code Unit 个数 每个 Code Unit 前缀 示例 Code Point 示例 Code Unit 序列
7 位以内 (00xEF) 1 0b0 0b0zzz zzzz 0b0zzz zzzz
8 到 11 位 (0x800x07FF) 2 第一个 0b110,剩下的 0b10 0b0yyy yyzz zzzz 0b110y yyyy 10zz zzzz
12 到 16 位 (0x08000xFFFF) 3 第一个 0b1110,剩下的 0b10 0bxxxx yyyy yyzz zzzz 0b1110 xxxx 10yy yyyy 10zz zzzz
17 到 21 位 (0x1000010FFFF) 4 第一个 0b11110,剩下的 0b10 0b000w wwxx xxxx yyyy yyzz zzzz 0b1111 0www 10xx xxxx 10yy yyyy 10zz zzzz

解码时,拿到每个 Code Unit 的前缀,就晓得这是对应第几个 Code Unit:

  • 前缀是 0b0,阐明这个 Code Point 是一个 Code Unit 组成
  • 前缀是 0b110,阐明这个 Code Point 是两个 Code Unit 组成,前面还会有 1 个 0b10 前缀的 Code Unit
  • 前缀是 0b1110,阐明这个 Code Point 是三个 Code Unit 组成,前面还会有 2 个 0b10 前缀的 Code Unit
  • 前缀是 0b11110,阐明这个 Code Point 是四个 Code Unit 组成,前面还会有 3 个 0b10 前缀的 Code Unit

UTF-8 的一个 Code Unit,刚好转换成 1 个字节,因而不须要思考字节序。

参考上表,对于 ASCII 范畴内的字符,应用 ASCII 和 UTF-8 编码的后果是一样的。所以 UTF-8 是 ASCII 的超集,应用 ASCII 编码的字节流也能够应用 UTF-8 解码。

UTF-8 与 UTF-16 比照

Code Point 范畴 UTF-8 编码长度 UTF-16 编码长度
7 位以内 (0x000xEF) 1 2
8 到 11 位 (0x00800x07FF) 2 2
12 到 16 位 (0x08000xFFFF) 3 2
17 到 21 位 (0x1000010FFFF) 4 4

能够看出只有在 0x000xEF 范畴的字符,UTF-8 编码比 UTF-16 短;而在 0x08000xFFFF 范畴内,UTF-8 编码是比 UTF-16 长的。

而中文次要在 0x4E000x9FFF,如果写一篇文档,全都是中文,一个英文字母和符号都没有。那应用 UTF-8 编码,可能比 UTF-16 编码还要多占用一半的空间。


相干文章:

  • Unicode 标准化
  • Unicode 与编程语言
  • 字节程序标记
  • 字节序
  • Unicode 与 UCS
退出移动版