Unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,为每种语言中的每个字符设定了统一并且唯一的二进制编码。
Unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990 年开始研发,1994 年正式公布。
起源
因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用 8 个比特(bit)作为一个字节(byte)。一个字节能表示的最大的整数就是 255(2^8-1=255),而 ASCII 编码,占用 0 – 127 用来表示大小写英文字母、数字和一些符号,这个编码表被称为 ASCII 编码,比如大写字母 A 的编码是 65,小写字母 z 的编码是 122。
如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和 ASCII 编码冲突,所以,中国制定了 GB2312 编码,用来把中文编进去。
类似的,日文和韩文等其他语言也有这个问题。为了统一所有文字的编码,Unicode 应运而生。Unicode 把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode 通常用两个字节表示一个字符,原有的英文编码从单字节变成双字节,只需要把高字节全部填为 0 就可以。
因为 Python 的诞生比 Unicode 标准发布的时间还要早,所以最早的 Python 只支持 ASCII 编码,普通的字符串’ABC’在 Python 内部都是 ASCII 编码的。
Unicode 是为了解决传统的字符编码方案的局限而产生的,例如 ISO 8859 所定义的字符虽然在不同的国家中广泛地使用,可是在不同国家间却经常出现不兼容的情况。很多传统的编码方式都有一个共同的问题,即容许电脑处理双语环境(通常使用拉丁字母以及其本地语言),但却无法同时支持多语言环境(指可同时处理多种语言混合的情况)。
Unicode 编码包含了不同写法的字,如“a/ɑ”、“户/户/戸”。然而在汉字方面引起了一字多形的认定争议。
在文字处理方面,统一码为每一个字符而非字形定义唯一的代码(即一个整数)。换句话说,统一码以一种抽象的方式(即数字)来处理字符,并将视觉上的演绎工作(例如字体大小、外观形状、字体形态、文体等)留给其他软件来处理,例如网页浏览器或是文字处理器。
几乎所有电脑系统都支持基本拉丁字母,并各自支持不同的其他编码方式。Unicode 为了和它们相互兼容,其首 256 字符保留给 ISO 8859-1 所定义的字符,使既有的西欧语系文字的转换不需特别考量;并且把大量相同的字符重复编到不同的字符码中去,使得旧有纷杂的编码方式得以和 Unicode 编码间互相直接转换,而不会丢失任何信息。举例来说,全角格式区段包含了主要的拉丁字母的全角格式,在中文、日文、以及韩文字形当中,这些字符以全角的方式来呈现,而不以常见的半角形式显示,这对竖排文字和等宽排列文字有重要作用。
在表示一个 Unicode 的字符时,通常会用“U+”然后紧接着一组十六进制的数字来表示这一个字符。在基本多文种平面(英文为 Basic Multilingual Plane,简写 BMP。它又简称为“零号平面”, plane 0)里的所有字符,要用四位十六进制数(例如 U+4AE0,共支持六万多个字符);在零号平面以外的字符则需要使用五位或六位十六进制数了。旧版的 Unicode 标准使用相近的标记方法,但却有些微的差异:在 Unicode 3.0 里使用“U-”然后紧接着八位数,而“U+”则必须随后紧接着四位数。
作用
能够使计算机实现跨语言、跨平台的文本转换及处理。
层次
Unicode 编码系统,可分为编码方式和实现方式两个层次。
方式
Unicode 是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。目前的 Unicode 字符分为 17 组编排,0x0000 至 0x10FFFF,每组称为平面(Plane),而每平面拥有 65536 个码位,共 1114112 个。然而目前只用了少数平面。UTF-8、UTF-16、UTF-32 都是将数字转换到程序数据的编码方案。
通用字符集(Universal Character Set, UCS)是由 ISO 制定的 ISO 10646(或称 ISO/IEC 10646)标准所定义的标准字符集。UCS-2 用两个字节编码,UCS-4 用 4 个字节编码。
历史上存在两个独立的尝试创立单一字符集的组织,即国际标准化组织(ISO)和多语言软件制造商组成的统一码联盟。前者开发的 ISO/IEC 10646 项目,后者开发的统一码项目。因此最初制定了不同的标准。
1991 年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从 Unicode 2.0 开始,Unicode 采用了与 ISO 10646-1 相同的字库和字码;ISO 也承诺,ISO 10646 将不会替超出 U+10FFFF 的 UCS-4 编码赋值,以使得两者保持一致。两个项目仍都存在,并独立地公布各自的标准。但统一码联盟和 ISO/IEC JTC1/SC2 都同意保持两者标准的码表兼容,并紧密地共同调整任何未来的扩展。在发布的时候,Unicode 一般都会采用有关字码最常见的字型,但 ISO 10646 一般都尽可能采用 Century 字型。
UCS-4 根据最高位为 0 的最高字节分成 27=128 个组(group)。每个 group 再根据次高字节分为 256 个平面(plane)。每个平面根据第 3 个字节分为 256 行 (row),每行有 256 个码位(cell)。group 0 的平面 0 被称作 BMP(Basic Multilingual Plane)。如果 UCS-4 的前两个字节为全零,那么将 UCS-4 的 BMP 去掉前面的两个零字节就得到了 UCS-2。每个平面有 216=65536 个码位。Unicode 计划使用了 17 个平面,一共有 17×65536=1114112 个码位。在 Unicode 5.0.0 版本中,已定义的码位只有 238605 个,分布在平面 0、平面 1、平面 2、平面 14、平面 15、平面 16。其中平面 15 和平面 16 上只是定义了两个各占 65534 个码位的专用区(Private Use Area),分别是 0xF0000-0xFFFFD 和 0x100000-0x10FFFD。所谓专用区,就是保留给大家放自定义字符的区域,可以简写为 PUA。
平面 0 也有一个专用区:0xE000-0xF8FF,有 6400 个码位。平面 0 的 0xD800-0xDFFF,共 2048 个码位,是一个被称作代理区(Surrogate)的特殊区域。代理区的目的用两个 UTF-16 字符表示 BMP 以外的字符。在介绍 UTF-16 编码时会介绍。
如前所述在 Unicode 5.0.0 版本中,238605-65534*2-6400-2048=99089。余下的 99089 个已定义码位分布在平面 0、平面 1、平面 2 和平面 14 上,它们对应着 Unicode 定义的 99089 个字符,其中包括 71226 个汉字。平面 0、平面 1、平面 2 和平面 14 上分别定义了 52080、3419、43253 和 337 个字符。平面 2 的 43253 个字符都是汉字。平面 0 上定义了 27973 个汉字。
在 Unicode 中:汉字“字”对应的数字是 23383(十进制),十六进制表示为 5B57。在 Unicode 中,我们有很多方式将数字 23383 表示成程序中的数据,包括:UTF-8、UTF-16、UTF-32。UTF 是“Unicode Transformation Format”的缩写,可以翻译成 Unicode 字符集转换格式,即怎样将 Unicode 定义的数字转换成程序数据。
例如,“汉字”对应的数字是 0x6c49 和 0x5b57,而编码的程序数据是:
这里用 char、char16_t、char32_t 分别表示无符号 8 位整数,无符号 16 位整数和无符号 32 位整数。UTF-8、UTF-16、UTF-32 分别以 char、char16_t、char32_t 作为编码单位。(注: char16_t 和 char32_t 是 C++ 11 标准新增的关键字。如果你的编译器不支持 C++ 11 标准,请改用 unsigned short 和 unsigned long。)“汉字”的 UTF-8 编码需要 6 个字节。“汉字”的 UTF-16 编码需要两个 char16_t,大小是 4 个字节。“汉字”的 UTF-32 编码需要两个 char32_t,大小是 8 个字节。根据字节序的不同,UTF-16 可以被实现为 UTF-16LE 或 UTF-16BE,UTF-32 可以被实现为 UTF-32LE 或 UTF-32BE。下面介绍 UTF-8、UTF-16、UTF-32、字节序和 BOM。
UTF-8
UTF-8 以字节为单位对 Unicode 进行编码。从 Unicode 到 UTF-8 的编码方式如下:
Unicode 编码(十六进制)
UTF-8 字节流(二进制)
000000-00007F
0xxxxxxx
000080-0007FF
110xxxxx 10xxxxxx
000800-00FFFF
1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8 的特点是对不同范围的字符使用不同长度的编码。对于 0x00-0x7F 之间的字符,UTF-8 编码与 ASCII 编码完全相同。UTF-8 编码的最大长度是 4 个字节。从上表可以看出,4 字节模板有 21 个 x,即可以容纳 21 位二进制数字。Unicode 的最大码位 0x10FFFF 也只有 21 位。
例 1:“汉”字的 Unicode 编码是 0x6C49。0x6C49 在 0x0800-0xFFFF 之间,使用 3 字节模板:1110xxxx 10xxxxxx 10xxxxxx。将 0x6C49 写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的 x,得到:11100110 10110001 10001001,即 E6 B1 89。
例 2:Unicode 编码 0x20C30 在 0x010000-0x10FFFF 之间,使用 4 字节模板:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。将 0x20C30 写成 21 位二进制数字(不足 21 位就在前面补 0):0 0010 0000 1100 0011 0000,用这个比特流依次代替模板中的 x,得到:11110000 10100000 10110000 10110000,即 F0 A0 B0 B0。