首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unicode 及其编码方案

Unicode 及其编码方案

作者头像
疯狂的技术宅
发布2019-03-28 10:42:46
1.5K0
发布2019-03-28 10:42:46
举报
文章被收录于专栏:京程一灯京程一灯

前言

Unicode 标准有上千页,还有几十页的补充附录、报告和注解。想要深入了解 Unicode,确实要下些功夫。

本文不准备深入地讲述 Unicode 相关的细节,只准备简要讲述 Unicode 编码相关的内容,以满足日常编程中处理 Unicode 字符编码的需求。

预备知识

编码模型

  1. Character Repertoire:字符组成的列表。
  2. Coded Character Set(CCS):将字符与唯一数值的关联起来的映射。其中的唯一数值称为 Code Point。 Unicode 是这一层的概念。
  3. Character Encoding Form(CEF):将由一个或多个 Code Unit(定长的 Bit 序列)组成的序列与 Code Point 关联起来的映射。 UTF-8 / UTF-16 / UTF-32 是这一层的概念。
  4. Character Encoding Scheme(CES):将 Code Unit 与 Octet 关联起来的映射。CEF 是为了满足基于 Octet 的文件系统的存储需求和基于 Octet 的网络的传输需求。

Octet 是什么? Octet 表示 8 Bit 的二进制流。那为什么不用 Byte?Byte 不一定是 8 Bit,只是现代计算机的事实标准使用 8 Bit 来代表一个 Byte。在很多技术规格文献中,为了避免产生歧义,更倾向于使用 Octet 这个术语来强调 8 Bit 的二进制流。

更多细节可阅读「字符编码 From Wikipedia」。

大小端

大小端的说法源自《格列佛游记》 —— 鸡蛋通常一端大一端小,小人国的人们对于剥蛋壳时应从哪一端开始剥起有着不一样的看法。

同样,人们对于传输多字节数据时,是先传高位字节(大端)还是先传低位字节(小端)也有着不一样的看法,这就是「大小端」的由来。

对于多字节数据来说,如果先操作高位字节,则称为大端模式;反之,则称为小端模式。

Unicode

Unicode —— A computing industry standard for providing a unique code point for each character. —— wikipedia

Unicode 中的 Code Point

预备知识中提到了 Code Point 的概念,这里不再解释。

Unicode 中的 Code Point 通常使用 U+Hex 的形式表示,比如:

  • 拉丁字母 A 的 Code Point 为 U+0041
  • 希腊字母 θ 的 Code Point 为 U+03B8
  • 中文单字 我 的 Code Point 为 U+6222

Unicode 中的 Plane

Plane 译为平面。

根据字符的使用频率,Unicode 被划分为了 17 个 Plane,每个 Plane 中包含 2^16 个 Code Point。

通过 0 ~ 16 这 17 个十进制数为 Plane 编号:

  • Basic(空间范围:U+0000 ~ U+FFFF):
    • U+D800U+DFFF:代理区。
    • U+E000U+F8FF:未被使用,留给第三方定义私有字符,以避免和 Unicode 冲突。
    • Plane 0 —— Basic Multilingual Plane(BMP)。其中包含绝大多数现代字符,比如拉丁文、斯拉夫文、希腊文、汉字(中国),日文、朝鲜文、阿拉伯文、希伯来文、梵文(印度)等。
  • Supplementary(空间范围:U+010000 ~ U+10FFFF):
    • Plane 1 —— Supplementary Multilingual Plane(SMP)。其中包含历史上的文字,比如苏美尔楔形文字、埃及象形文字、Emoji 以及其他符号。
    • Plane 2 —— Supplementary Ideographic Plane(SIP)。其中包含不常用的和历史遗留的汉字字符。
    • Plane 3 ~ 13 —— 未被使用。
    • Plane 14 —— Supplement Special-purpose Plane(SSP)。其中包含少量用于格式化的字符。
    • Plane 15 —— Supplement Private Use Area Plane A(SPUA-A)。未被使用,留给第三方定义私有字符,以避免和 Unicode 冲突。
    • Plane 16 —— Supplement Private Use Area Plane B(SPUA-B)。未被使用,留给第三方定义私有字符,以避免和 Unicode 冲突。

下图为 Unicode Plane 的整体布局,从左到右,从上至下,编号依次为 0 ~ 16,其中:

  • 一个像素表示 1 个 Code Point
  • 一个小方块表示 2^8 个 Code Point(只为保证视觉一致性,无特殊意义)
  • 一个大方块表示一个 Plane,其中包含 2^16 个 Code Point
  • 白色表示未用空间
  • 蓝色表示已用空间
  • 绿色表示自用空间
  • 红色区域表示代理区,空间范围 U+D800U+DFFF(描述 UTF-16 编码方案的时候会提及)

Unicode Planes

Unicode 编码方案

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

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

UTF-16 中,每个 Code Point 使用 2 个字节或 4 个字节表示。

Code Point 范围

占用字节数量

U+0000 - U+FFFF

2

U+010000 - U+10FFFF

4

UTF-16 编码方案中,涉及到一个概念 —— 代理区。代理区位于 BMP 中,空间范围 U+D800U+DFFF,空间大小 2^11,用于映射 Supplementary Plane 中的所有 Code Point。

Q:代理区空间大小 2^11,如何做到映射空间大小2^20 的 Supplementary Plane? A:将 U+D800U+DFFF(2^11) 这个范围内的值分为两部分:

  • U+D800U+DBFF(空间大小 2^10),称为高位(H)
  • U+DC00U+DFFF(空间大小 2^10),称为低位(L)

对高位和低位做迪卡尔积,可以得到 2^20 种组合,这样就可以映射 Supplementary Plane 了。

Code Point 编码方法

将 Code Point 编码为 Code Unit。

  • U+0000U+FFFF 范围内的 Code Point,直接用 2 个字节表示。
  • U+010000U+10FFFF 范围内的 Code Point 通过以下形式编码,再用 4 个字节表示。
    1. Code Point 减去 0x10000,获得一个在 0x000000xFFFFF 范围中的值,这个值可以转换为 20 位的二进制数。
    2. 20 位的二进制数的高 10 位与 0xD800 相加,获得高位。
    3. 20 位的二进制数的低 10 位与 0xDC00 相加,获得低位。
    4. 高位与低位组合后,刚好可以用 4 个字节表示。
Code Unit 解码方法

将 Code Unit 解码为 Code Point。

因为有了高低位,解析字节流时就涉及到了预备知识里提到的大小端问题。

在解析使用 UTF-16 大端编码的字节流时,首先判断 Code Unit 是否在 U+D800U+DBFF 范围内:

  • 如果是,则与其后相邻的 Code Unit 放在一起解码。
  • 如果不是,直接解码。

在解析使用 UTF-16 小端编码的字节流时,首先判断 Code Unit 是否在 U+DC00U+DFFF 范围内:

  • 如果是,则与其后相邻的 Code Unit 放在一起解码。
  • 如果不是,直接解码。

UTF-8

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 Point 编码为 Code Unit。

  • U+0000U+007F 范围内的 Code Point,使用 1 个字节表示,最高位设为 0,低 7 位设为对应的 Code Point。
  • U+0080U+10FFFF 范围内的 Code Point,使用 2~4 个字节表示。设当前所使用的字节数为 n,第一个字节的前高 n 位设为 1,第高 n + 1 位设为 0,后续字节的高 2 位均设为 10。剩余没有提及的二进制位,从低位到高位依次填入对应的 Code Point,空位补 0。

编码方法看似繁冗,其实非常简单。以汉字 为例:

  • 汉字 对应的 Code Point 为 U+6211,转换为二进制 110001000010001
  • U+6211U+0800U+FFFF 间,使用 3 个字节表示,具体格式为 1110xxxx10xxxxxx10xxxxxx
  • 从低位到高位依次填入对应的 Code Point,空位补 0。得到对应 UTF-8 编码 111001101000100010010001,转换为十六进制为 E6 88 91
Code Unit 的解码方法

将 Code Unit 解码为 Code Point。

  • Code Unit 最高位为 0,直接解码
  • Code Unit 最高位为 1,统计连续的 1 的数量,以获得需要组合解码的 Code Unit 数量。根据 Code Unit 数量读取所需 Code Unit,截取 Code Unit 中的有效位,进行连接,以获取对应 Code Point。

最后

Unicode 的编码方案很好玩,是吧?


往期精选文章

一小时内搭建一个全栈Web应用框架

全栈工程师技能大全

一个治愈JavaScript疲劳的学习计划

推翻JavaScript中的三座大山:作用域篇

掌握Chrome开发工具:新一代前端开发技术

WEB前端性能优化常见方法

在 Vue 中创建自定义输入

干货:CSS 专业技巧

四步实现React页面过渡动画效果

理解CSS模块化



小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 京程一灯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 预备知识
    • 编码模型
      • 大小端
      • Unicode
        • Unicode 中的 Code Point
          • Unicode 中的 Plane
          • Unicode 编码方案
            • UTF-32
              • UTF-16
                • Code Point 编码方法
                • Code Unit 解码方法
              • UTF-8
                • Code Point 编码方法
                • Code Unit 的解码方法
            • 最后
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档