Unicode 标准有上千页,还有几十页的补充附录、报告和注解。想要深入了解 Unicode,确实要下些功夫。
本文不准备深入地讲述 Unicode 相关的细节,只准备简要讲述 Unicode 编码相关的内容,以满足日常编程中处理 Unicode 字符编码的需求。
Octet 是什么? Octet 表示 8 Bit 的二进制流。那为什么不用 Byte?Byte 不一定是 8 Bit,只是现代计算机的事实标准使用 8 Bit 来代表一个 Byte。在很多技术规格文献中,为了避免产生歧义,更倾向于使用 Octet 这个术语来强调 8 Bit 的二进制流。
更多细节可阅读「字符编码 From Wikipedia」。
大小端的说法源自《格列佛游记》 —— 鸡蛋通常一端大一端小,小人国的人们对于剥蛋壳时应从哪一端开始剥起有着不一样的看法。
同样,人们对于传输多字节数据时,是先传高位字节(大端)还是先传低位字节(小端)也有着不一样的看法,这就是「大小端」的由来。
对于多字节数据来说,如果先操作高位字节,则称为大端模式;反之,则称为小端模式。
Unicode —— A computing industry standard for providing a unique code point for each character. —— wikipedia
预备知识中提到了 Code Point 的概念,这里不再解释。
Unicode 中的 Code Point 通常使用 U+Hex 的形式表示,比如:
U+0041
U+03B8
U+6222
Plane 译为平面。
根据字符的使用频率,Unicode 被划分为了 17 个 Plane,每个 Plane 中包含 2^16 个 Code Point。
通过 0 ~ 16 这 17 个十进制数为 Plane 编号:
U+0000
~ U+FFFF
):U+D800
~U+DFFF
:代理区。U+E000
~U+F8FF
:未被使用,留给第三方定义私有字符,以避免和 Unicode 冲突。U+010000
~ U+10FFFF
):下图为 Unicode Plane 的整体布局,从左到右,从上至下,编号依次为 0 ~ 16,其中:
U+D800
~U+DFFF
(描述 UTF-16 编码方案的时候会提及)Unicode Planes
Unicode 只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,而 Unicode 字符如何存取,Unicode 是不关心的。
为了解决 Unicode 字符的问题,引入了 Unicode 编码方案。Unicode 编码方案中比较流行的是 Unicode Transformation Formats(UTF)。
UTF 会以数字作为后缀,如 UTF-8 / UTF-16 / UTF-32。其中的数字表示 Code Unit 的大小,也就是 Code Unit 对应的 Bit 序列的长度(在预备知识中提到,Code Unit 是定长的 Bit 序列)。对 UTF-8 来言,Code Unit 的大小是 8;对 UTF-16 而言,Code Unit 的大小是 16;诸如此类。
UTF-32 中,每个 Code Point 使用 4 个字节表示,字节内容与 Code Point 一一对应。这是最简单直接的编码方案。
举例:
字符 | Code Point | 编码 |
---|---|---|
A | U+0041 | 0x0000 0041 |
θ | U+03B8 | 0x0000 03B8 |
我 | U+6211 | 0x0000 6211 |
? | U+2003E | 0x0002 003E |
但是,使用四个字节来存储单个字符会极大的浪费存储空间。由于这个缺点,这种编码方案并不常用 。
UTF-16 中,每个 Code Point 使用 2 个字节或 4 个字节表示。
Code Point 范围 | 占用字节数量 |
---|---|
U+0000 - U+FFFF | 2 |
U+010000 - U+10FFFF | 4 |
UTF-16 编码方案中,涉及到一个概念 —— 代理区。代理区位于 BMP 中,空间范围 U+D800
~U+DFFF
,空间大小 2^11,用于映射 Supplementary Plane 中的所有 Code Point。
Q:代理区空间大小 2^11,如何做到映射空间大小2^20 的 Supplementary Plane? A:将
U+D800
~U+DFFF
(2^11) 这个范围内的值分为两部分:
U+D800
~U+DBFF
(空间大小 2^10),称为高位(H)U+DC00
~U+DFFF
(空间大小 2^10),称为低位(L)对高位和低位做迪卡尔积,可以得到 2^20 种组合,这样就可以映射 Supplementary Plane 了。
将 Code Point 编码为 Code Unit。
U+0000
~U+FFFF
范围内的 Code Point,直接用 2 个字节表示。U+010000
~U+10FFFF
范围内的 Code Point 通过以下形式编码,再用 4 个字节表示。0x10000
,获得一个在 0x00000
~0xFFFFF
范围中的值,这个值可以转换为 20 位的二进制数。0xD800
相加,获得高位。0xDC00
相加,获得低位。将 Code Unit 解码为 Code Point。
因为有了高低位,解析字节流时就涉及到了预备知识里提到的大小端问题。
在解析使用 UTF-16 大端编码的字节流时,首先判断 Code Unit 是否在 U+D800
~U+DBFF
范围内:
在解析使用 UTF-16 小端编码的字节流时,首先判断 Code Unit 是否在 U+DC00
~U+DFFF
范围内:
UTF-8 中,每个 Code Point 使用 1~4 个字节表示。
Code Point 范围 | 占用字节数量 | Code Point 位数 | 字节 1 | 字节 2 | 字节 3 | 字节 4 |
---|---|---|---|---|---|---|
U+0000 - U+007F | 1 | 7 | 0xxxxxxx | |||
U+0080 - U+07FF | 2 | 11 | 110xxxxx | 10xxxxxx | ||
U+0800 - U+FFFF | 3 | 16 | 1110xxxx | 10xxxxxx | 10xxxxxx | |
U+010000 - U+10FFFF | 4 | 21 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |
将 Code Point 编码为 Code Unit。
U+0000
~U+007F
范围内的 Code Point,使用 1 个字节表示,最高位设为 0,低 7 位设为对应的 Code Point。U+0080
~U+10FFFF
范围内的 Code Point,使用 2~4 个字节表示。设当前所使用的字节数为 n,第一个字节的前高 n 位设为 1,第高 n + 1 位设为 0,后续字节的高 2 位均设为 10。剩余没有提及的二进制位,从低位到高位依次填入对应的 Code Point,空位补 0。编码方法看似繁冗,其实非常简单。以汉字 我 为例:
U+6211
,转换为二进制 110001000010001
。U+6211
在 U+0800
~U+FFFF
间,使用 3 个字节表示,具体格式为 1110xxxx10xxxxxx10xxxxxx
。111001101000100010010001
,转换为十六进制为 E6 88 91
。将 Code Unit 解码为 Code Point。
Unicode 的编码方案很好玩,是吧?
往期精选文章 |
---|
一小时内搭建一个全栈Web应用框架 |
全栈工程师技能大全 |
一个治愈JavaScript疲劳的学习计划 |
推翻JavaScript中的三座大山:作用域篇 |
掌握Chrome开发工具:新一代前端开发技术 |
WEB前端性能优化常见方法 |
在 Vue 中创建自定义输入 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
理解CSS模块化 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。