字符集编码的发展与简介(三):Unicode

其他章节:
字符集编码的发展与简介(一):ASCII码
字符集编码的发展与简介(二):ANSI
字符集编码的发展与简介(三):UNICODE
字符集编码的发展与简介(四):字符集编码模型



什么是Unicode

ANSI 编码解决了不同国家地区的编码需求,但是不适合跨地区交流,Unicode 应运而生:对于地球上任意一个字符,都给它一个唯一的数值。[8]

Unicode 为世界上所有字符都分配了一个唯一的数字编号,因此又被称为万国码 [1] 。整个编码范围 从 U+0000 到 U+10FFFF 包括 1,114,112 个代码点。减去 66 个作为非字符位置,减去 2,048 个为代理保留的范围,Unicode 代码空间包括 1,111,998 个可分配的代码点。[12]

Unicode 编码字符集(coded character set,可简写为CCS) 根据整数值进行编码,按照惯例,Unicode 代码点以十六进制表示法表示,至少四位数字,前面有 “U+”;因此,例如,“U+0345”、“U+10345” 和 “U+20345”。同样按照惯例,任何超过四位数字的前面的0都会被省略,即 “U+0456” 是合法的,但 “U+03456” 则是非法的。Unicode 中的每个字符都可以通过其 代码点(codepoint) 或名称进行唯一标识。[12,23]

Unicode 代码空间(codespace) 的范围从 U+0000 到 U+10FFFF。借用 ISO/IEC 10646 的术语,代码空间以 17个平面(每个平面64K个代码点)来描述。因此,平面 0 包括代码点 U+0000 ~ U+FFFF,平面 1 包括代码点 U+10000 ~ U+1FFFF,其他平面也依此类推。[12]

在 Unicode 的原始设计中,所有字符的代码点范围都在 U+0000 ~ U+FFFF 范围内。与此一致,平面 0 被设置为代码空间中所有最常用字符被编码的部分,并被指定为 基本多语言平面(Basic Multilingual Plane,简写为BMP) 。代码空间的其余部分,即平面 1 到 16,统称为 补充平面(Supplementary Planes) 。在 TUS 3.0.1 之前,字符仅在 BMP 中分配,在 TUS 3.1中,字符才首次在补充平面中分配。[12]

简单来说,Unicode 就是编码字符集,建立了字符与编号之间的联系。至于编号怎么对应到二进制,就需要建立一套规则,现在 Unicode 有三种编码形式:UTF-8,UTF-16,UTF-32。(UTF: Unicode Transformation Format)

关于 Unicode,参考文献 [23] 是 Unicode 官网提供的一些学习资料,其中还有一些关于 Unicode 的学习视频和演讲,如想了解转至该文献。

Unicode的三种编码形式

(1)UTF-16

由于 Unicode 的早期历史和最初的设计目标是具有统一的16位编码,因此今天许多人认为 Unicode 仅为 16 位编码。Unicode 现在支持三种不同的编码形式,但通常没有一种比其他形式更优先。但是,UTF-16 可能被认为具有特殊的重要性,因为它是与 Unicode 的流行印象相匹配的编码形式 [12],尽管这种印象是有误的。

目前的 UTF-16 使用变长字节表示,使用一个或两个 16 位代码单元(UTF-16 的每个代码单元是 2 字节,也就是 16 位)。平均而言,最常用的字符是在基本多语言平面中编码的。因此,对于许多文本来说,从来没有必要引用 U+FFFF 以上的字符。也就是说,大部分字符可以用 2 个字节(16 位)表示,只有一小部分不常用、编号高的字符需要用 4 个字节(32 位)来表示。

为了表示 Unicode 引入的增补平面中的码点(平面 1 ~ 16,码点范围为 0x10000 ~ 0x10FFFF ), UTF-16 中使用 “代理机制” 来解决这个问题。其基本思想就是使用两个基本平面中未定义(或未使用)的码点合起来代替一个增补平面的码点。[16]

基本平面中的字符(除 U+D800 ~ U+DFFF 外)仍然使用固定两字节直接映射的方式编码,即基本平面字符的字符编号(码点值)就是字符编码。增补平面的字符使用“代理机制”这一编码算法进行编码,使用代理机制后需要使用 2 个代码单元(4 字节)来表示 Unicode 增补平面中的码点。[16]

基本平面中用来代替增补平面码点的未使用的码点区域被称为代理区(surrogate code units),其码点范围为 0xD800 ~ 0xDFFF,共 2048 个码点。UTF-16 将这些分为两半:0xD800 ~ 0xDBFF 被称为高代理码点(high surrogates),0xDC00 ~ 0xDFFF 被称为低代理码点(low surrogates)。这两个范围中每个区域中都有 1024 个代码值,则有 1024×1024 = 1048576 种可能的组合,这与补充平面中的代码点数完全匹配。因此,在 UTF-16 中,以一对代理码点,或称为 代理项对(surrogate pair) 的方式对补充平面中的字符进行编码。[12]
UTF16中的代理区

应该指出的是,代理对必须由高代理项和低代理项组成,如果在数据中遇到未配对的高或低代理项,则将其视为格式不正确,不得解释为字符。[12]

参考文献 [16] 中详细介绍了UTF-16的编码原理,如想了解转至该文章。

目前,UTF-16 是使用最多的编码形式,Windows 底层中都是使用的 Unicode 的 UTF-16 编码形式[26]

(2)UTF-8

一开始开发 UTF-8 编码形式是为了使一些8位的软件也能正确使用 Unicode 字符集。特别是对于某些文件系统,其中某些字节值具有特殊意义(例如,0x2A 在 ASCII 中是 “*”,通常用于表示通配符)[12]目前,UTF-8 在 HTML 和类似协议中很受欢迎 [23]

对 UTF-8 而言,一个代码单元为 8 位。UTF-8 使用变长字节表示,即使用的字节数可变,从 1 到 4 个字节长度不等,代码点小的使用的字节就少,代码点大的使用的字节就多。
UTF-8 中,序列初始字节和非初始字节有明显不同的标记,因此可以立即确定 UTF-8 代码单元是序列中的初始字节还是后续字节。其次,UTF-8 序列中的第一个字节根据其范围提供了序列长度的明确指示。[12]

上述的两个特征结合起来使得处理 UTF-8 序列非常高效。与 UTF-16 一样,这种编码形式比各种遗留的多字节编码(ANSI 编码)要高效得多。[12]

UTF-8 编码方式的一个有趣的附加效果是:ASCII 编码的数据也自动符合 UTF-8 [12] 。这也就是说,只有 UTF-8 兼容 ASCII;UTF-16 和 UTF-32 都不兼容 ASCII,因为他们没有单字节编码。

Unicode 代码点范围(十六进制)UTF-8 字节流(二进制)
U+0000 ~ U+007F0xxxxxxx
U+0080 ~ U+07FF110xxxxx 10xxxxxx
U+0800 ~ U+FFFF1110xxxx 10xxxxxx 10xxxxxx
U+10000 ~ U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

上表是 UTF-8 对 Unicode 字符集的编码形式,对于前 0x7F 的字符,UTF-8 编码和 ASCII 码是一一对应的。如果一个字符在 0x0800 ~ 0xFFFF 之间,那转化到 UTF-8 需要用三字节模板,使用 16 个码位,每个 x 就是一个码位。[15]

(3)UTF-32

UTF-32 编码形式很容易解释:每个代码点都使用等于代码点标量值的 32 位整数进行编码 [12] 。也就是说,不管什么字符直接用四个字节来存储。比如 “马” 的 Unicode 为:U+9A6C,直接转化为二进制,就表示为:00000000 00000000 10011010 01101100。

(4)各种编码形式的对比分析

代码点范围UTF-8UTF-16UTF-32
U+0000 ~ U+007F(2进制:0 ~ 27 ;10进制:0 ~ 128)1 byte2 byte4 byte
U+0080 ~ U+07FF(2进制:27 ~ 211;10进制:129 ~ 2048)2 byte2 byte4 byte
U+0800 ~ U+FFFF(2进制:211 ~ 216;10进制:2049 ~ 65536)3 byte2 byte4 byte
U+10000 ~ U+10FFFF(2进制:216 ~ 17×216;10进制:65537 ~ 1114112)4 byte4 byte4 byte

从上表中也可以看出,当程序中大量使用英文字母时,UTF-8 是更为合适的,因为其占用了更少的字节数,并且与 ASCII 编码完全兼容。尤其是当程序需要与仅支持 8 位数据的软件相互支持时,UTF-8 就成为了 Unicode 的唯一选择。

而对于大量使用基本多语言平面(BMP)内字符的程序,例如大量使用中文的软件中,对于 UTF-16 的编码形式,所有字符全部编码为 16 位,可以非常快速的完成代理项的测试。在存储方面也为多语言提供了良好的平衡。在这些因素下,许多支持Unicode的应用程序将 UTF-16 作为主要的编码形式。[12]

UTF-32 的优点在于每个字符的大小完全相同,永远不需要测试代码单元的值来确定它是否是序列的一部分,读取简单。但是其数据整体很大,浪费存储空间。UTF-32 的实际使用是非常少的。

Unicode与UCS发展史

Unicode与ISO

Unicode 项目始于 1988 年,是由来自多家公司的代表合作开发的、一种可以支持世界上所有脚本的单一字符集编码标准。1991年1月成立了 Unicode 联盟,并于同年 10 月发布了 Unicode 标准的第一版——TUS 1.0(Unicode 标准的编号版本通常引用为 “TUS x.y”,其中 x.y 是版本号。例如 TUS 3.1)。[12]

但其实早在 1984 年,一个 ISO/IEC 联合工作组就已经成立了,并开始制定支持世界上所有书写系统的国际字符集标准,这被称为通用字符集(Universal Character Set,简称为UCS)。在 1989 年,该国际组织发布了 ISO/IEC 10646 字符集,该标准使用 31 位代码空间,允许超过 2 亿个字符。[12]

后来人们意识到这两个组织正在努力实现类似的目标,即一个所有人都可以使用的通用标准。于是在 1991 年,ISO/IEC 工作组和 Unicode 联盟开始讨论这两个标准的合并,合并的完整细节经过多年制定,但最重要的问题很早就解决了。

Unicode 联盟一开始发布的 TUS 1.0 中是使用统一的 16 位编码形式设计的,最多允许 65,536 个字符,但与 ISO/IEC 10646 的 2 亿个字符还是差别很大。在合并初期,即 TUS 1.1 开始,这 65,536 个字符被合并整理为了 ISO/IEC 10646 的子集。换句话说,Unicode 定义的 65,536 个字符中的任何一个都可以在 ISO/IEC 10646 中被找到,但对于 64K 以上的编码空间, Unicode 是无法使用的,只能以 ISO/IEC 10646 的格式来定义字符。

在合并过程中,Unicode 联盟意识到 65,536 个字符不足以覆盖所有的中国文字,因此需要放弃一开始统一的16位编码形式,来提高字符集的容量。最终的解决方案是:两种标准都采用了经修订的16 位编码形式,即 UTF-16 和 UCS-2 。UCS-2 在本质上等同于 UTF-16 ,字符集也与 TUS 1.1 中的内容相同,但 UCS-2 没有代理项(关于这一名词见参考文献 [12] 的 4.1 和参考文献 [16] ),也就是说 UCS-2 只能表示基本平面(即 BMP,第 0 平面)中的 2^16 个码点。换句话说,UCS-2 是固定两个字节的,而 UTF-16 是变长的,即可能是两个字节,也可能是四个字节。

这种编码形式允许支持超过一百万个字符。ISO/IEC 10646 仍然可以容纳更多的字符,但是人们承认一百万个字符已经足够了。最终,ISO/IEC 标准通过永久保留代码空间的其余部分,正式限制了可分配字符的数量,使与 Unicode 字符集保持一致。

但在当时,还有很多 8 位的进程,无法正确处理 16 位数据。于是开发了一种 8 位编码形式,称为 UTF-8,它能够支持全部的潜在字符,并且被两个标准采用。

Unicode 中原始的单一统一 16 位编码被UTF-16和UTF-8取代,在 Unicode 的TUS 2.0 中正式确定(Unicode Consortium 1996)。该版本的语言仍然将 16 位编码形式作为主要表示形式,但其实内部发生了根本的变化。

自首次发布以来,ISO/IEC 10646 一直支持 32 位编码形式,称为 UCS-4,支持整个 ISO/IEC 10646 的代码空间。于是,Unicode 引入了一种 32 位编码形式,称为 UTF-32(见上一节),并在 Unicode 的 TUS 3.1 标准中正式采用。实际上,UCS-4 等同于 UTF-32,但代码空间除外:根据定义,UCS-4 可以表示 U+0000 ~ U+7FFFFFFFFF(整个 ISO/IEC 10646 代码空间)范围内的代码点,而 UTF-32 只能表示 U+0000 ~ U+10FFFF(整个 Unicode 代码空间)范围内的代码点。 [12]

合并的主要结果是 Unicode 现在与 ISO/IEC 10646 保持同步,并且现在支持基于 8 位、16 位和 32 位代码单元的三种编码形式[12]

到目前为止,Unicode 标准与国际标准 ISO/IEC 10646(即通用字符集,简称UCS)密切合作,以确保这两个标准是随时协调同步的,保证Unicode 编码形式与 ISO/IEC 10646 中定义的编码形式完全对应。[23]

Unicode 标准一直在继续发展到现在,工作仍在继续,目的是使该标准更加完整,涵盖世界上更多的书写系统,纠正细节上的错误,并使其更好地满足实施者的需求。Unicode 3.0 版于 2000 年发布(The Unicode Consortium 2000),引入了 10,000 多个新字符 [12] 。Unicode 3.1 版于 2001 发布,此版本又添加了 44,946 个新字符,使字符总数达到 94,140 个编码字符 [12] 。最新的版本是 Unicode15.0.0,于 2022.9.13 发布,此版本增加了 4,489 个字符,包括 20 个 emoji 和 4,193 个中日韩字符,目前 Unicode 的字符总数达到了 149,186个 [24]

Unicode编码方案

在上面两节中简单提到了,16 位和 32 位编码形式引发了与字节排序相关的问题(具体描述可以参考文献**[12]** )。因此,当涉及 16 位或 32 位代码单元时,这些代码单元可能会作为一组字节进行处理,并且这些字节在通过线路传输或存储在磁盘上之前必须按串行顺序排列。[12]

有两种方法可以对构成 16 位或 32 位代码单元的字节进行排序。一种是从高阶(最重要)字节开始,以低位(最不重要)字节结束,这通常被称为大端序(big-endian, BE)。另一种方式正好相反,通常被称为小端序(little-endian, LE)。对于16位和32位编码形式,特定编码形式的规范以及特定的字节顺序称为字符编码方案(character encoding scheme)[12]

对于 UTF-16 编码形式的数据,只能通过两种方式之一进行序列化。就它的实际组织方式而言,它必须是大端序或小端序。但是,Unicode允许以三种方式描述数据的编码方案:大端序、小端序和未指定端序,UTF-16 和 UTF-32 都是如此。[12]

因此,Unicode 提供了七种字符编码方案(Character encoding scheme,简称CES)

  • UTF-8
  • UTF-16BE
  • UTF-16LE
  • UTF-16
  • UTF-32BE
  • UTF-32LE
  • UTF-32

这里需要注意的是,“UTF-8”、“UTF-16” 和 “UTF-32” 可以通过两种含义使用:作为编码形式(encoding form)或作为编码方案(encoding scheme)。在大多数情况下,它的意思要么是明确的,要么是无关紧要的。但是在某些情况下,我们需要明确这代表什么含义。

BOM

为了解决程序在读取数据时,仍然不知道字节顺序问题,Unicode 将代码点 U+FEFF 指定为字节顺序标记(byte order mark, BOM)。因为不管是正序还是相反字节顺序,代码点 U+FEFF 和 U+FFFE 始终被保留为非字符。在文件或数据流的开头读取该标记,即可明确使用哪个字节顺序。[12]

例如,在 UTF-16BE 下,BOM 的字节序列为 0xFE 0xFF;在 UTF-16LE 下,BOM 被编码为 0xFF 0xFE;在 UTF-8 下,BOM 被编码为 0xEF 0xBB 0xBF;在 UTF-32BE 下,BOM 的字节序列为 0x00 0x00 0xFE 0xFF;在 UTF-32LE 下,BOM 的字节序列为 0xFE 0xFF 0x00 0x00。

通过上述标识,即可判断使用的是哪种 Unicode 编码方案。

因为在大多数传统编码标准(ANSI)中,字节序列 0xFE 0xFF 和 0xFF 0xFE 的可能性极小。因此,当文件以此值开头,程序基本可以推断数据是 Unicode 字符集,并且还能够推断出编码形式。[12]

当以这种使用 BOM 的方式来标识数据的字符集编码时,它被称为编码签名(encoding signature)


全文参考文献

[1] CSDN. 你真的懂Unicode和UTF-8是什么关系吗?来看看这个就彻底懂了![DB/OL]. (2018-11-19). https://blog.csdn.net/zhusongziye/article/details/84261211
[2] CSDN. Unicode和UTF-8的关系[DB/OL]. (2022-11-23). https://blog.csdn.net/song854601134/article/details/127994299
[3] 博客园. Unicode和UTF-8的关系[DB/OL]. (2019-05-12). https://www.cnblogs.com/tsingke/p/10853936.html
[4] 知乎. Unicode和utf8的关系是什么?[DB/OL]. (2022-10-30). https://www.zhihu.com/question/274104168/answer/2737298850
[5] 成都信息工程大学. 网页编码UTF-8,GBK,GB2312的区别[DB/OL]. (2015-10-17). http://jszx.cuit.edu.cn/NewsCont.asp?type=1009&id=20573
[6] 曹晖.字符集与字符编码标准[J].西北民族大学学报(自然科学版),2006,(03):36-42.
[7] CSDN. ANSI是什么编码?[DB/OL]. (2022-06-12). https://blog.csdn.net/weixin_38293850/article/details/125248400
[8] CSDN. ANSI和UNICODE编码区别[DB/OL]. (2022-08-20). https://blog.csdn.net/u010164190/article/details/126433688
[9] CSDN. ANSI是什么编码?[DB/OL]. (2020-08-07). https://blog.csdn.net/Liuqz2009/article/details/107861408
[10] JOEL SPOLSKY. The Absolute Minimum Every Software Developer Absolutely Positively Must Know About Unicode and Character Sets[DB/OL]. (2003-10-08). The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) – Joel on Software
[11] 腾讯云开发者社区. 编码、R与Windows(一) [DB/OL]. (2020-10-23). https://cloud.tencent.com/developer/article/1727834
[12] Computers & Writing Systems. Understanding Unicode [DB/OL]. (2001-06-13). http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter04a
[13] Computers & Writing Systems. Character set encoding basics [DB/OL]. (2001-06-13). https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter03
[14] 知乎. 字符集编码(一):Unicode之前 [DB/OL]. (2023-04-24). https://zhuanlan.zhihu.com/p/468843643
[15] 知乎. UTF-8到底是什么意思?uicode编码简介 [DB/OL]. (2020-05-03). https://zhuanlan.zhihu.com/p/137875615
[16] CSDN. Unicode编码详解(四):UTF-16编码[DB/OL]. (2022-02-12). https://blog.csdn.net/hyongilfmmm/article/details/112046816
[17] CSDN. ANSI、MBCS、UNICODE字符集[DB/OL]. (2020-05-23). https://blog.csdn.net/fenghaiyang198848/article/details/106301653
[18] Microsoft文档. 支持多字节字符集 (MBCS) [DB/OL]. (2023-06-16). https://learn.microsoft.com/zh-cn/cpp/text/support-for-multibyte-character-sets-mbcss?view=msvc-170
[19] Microsoft文档. Unicode 和多字节字符集 (MBCS) 支持 [DB/OL]. (2023-04-03). https://learn.microsoft.com/zh-cn/cpp/atl-mfc-shared/unicode-and-multibyte-character-set-mbcs-support?view=msvc-170
[20] CSDN. ANSI、MBCS、UNICODE字符集 [DB/OL]. (2020-05-23). https://blog.csdn.net/fenghaiyang198848/article/details/106301653
[21] Charles Petzold. Windows程序设计(第7版)[M]. 北京: 清华大学出版社, 2000.
[22] UNICODE官网. About the Unicode Consortium [DB/OL]. https://home.unicode.org/about-unicode/
[23] UNICODE官网. Unicode标准:技术介绍 [DB/OL]. (2019.08.23). https://www.unicode.org/standard/principles.html
[24] UNICODE官网. Unicode15.0.0 [DB/OL]. (2022.09.13). https://www.unicode.org/versions/Unicode15.0.0/
[25] CSDN. Unicode了解一下:码位分布[DB/OL]. (2018.04.21). https://blog.csdn.net/oyji1992/article/details/80030366
[26] Microsoft文档. 字符集 [DB/OL]. (2023-06-16). https://learn.microsoft.com/zh-cn/windows/win32/intl/character-sets
[27] 博客园. 汉字编码之GBK编码[DB/OL]. (2020-11-05). https://www.cnblogs.com/Malphite/p/13931511.html
[28] CSDN. 汉字编码之GBK编码(附完整码表)[DB/OL]. (2016-03-04). https://learn.microsoft.com/zh-cn/windows/win32/intl/character-sets
[29] Microsoft文档. SBCS 和 MBCS 数据类型 [DB/OL]. (2023-06-16). https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/sbcs-and-mbcs-data-types?view=msvc-170
[30] Microsoft文档. Unicode 和 MBCS [DB/OL]. (2023-06-16). https://learn.microsoft.com/zh-cn/cpp/text/unicode-and-mbcs?view=msvc-170

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值