天天看点

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

1. 字符编码术语

  • 字符集(character set): 可表达文本信息的元素集合,如字母,数字,符号,图像,控制字符等等。
  • 编码空间(code space): 可用来编码字符的数字值的范围,不同的编码,编码空间不同。如US-ASCII的编码空间是0x0 – 0x7F,Unicode的编码空间是0x0 – 0x10FFFF。
  • Unicode: 可表示2个意思,一个是表示一个标准,全球统一字符集(UCS)标准。另一个意思是表示发布这个标准的组织。本文没有特别区分,因为根据上下文很容易区分。
  • 代码点(code point): 来自代码空间中任意值都是一个代码点。注意,并不是每个代码点都编码了字符。如Unicode中代码点区间U+D800 – U+DFFF就没有编码到字符。
  • 代码值(code value): 使用特定的编码方法编码代码点后得到的值就是代码值。是指直接存储到文件中的值。
  • 代码单元(code unit): 编码值的最小单位。不同的编码方式,代码单元不一样。比如UTF-8的代码单元是8bit, 那么编码后的编码值可能就是1/2/3/4字节。UTF-16的代码单元是16bit, 那么编码后的编码值可能就是2/4字节。
  • UCS: Universal Character Set的简称,是ISO/IEC提出的全球统一字符集。现在等同于Unicode。
  • UTF: Unicode(或者)Transformation Format,就是编码Unicode标准的编码方式。如UTF-8,UTF-16LE,UTF-16BE,UTF-32等。
  • BOM: Byte Order Mark, 在文本文件的开头插入标准代码点,用来表示编码的字节序。

2. ASCII

全称是美国信息交换标准代码(American Standard Code for Information Interchange),对应的国际标准是ISO/IEC 646。它是一个单字节编码字符集。编码空间是0x00-0x7F,最高位为0。

详细字符表如下图,编码表示了所有大写字母,小写字母,数字,标点符号,以及控制字符等共127个字符。

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

3. ISO 8859标准系列

ISO 8859标准是基于标准ASCII进行扩展的单字节编码,具体点说就是把ASCII标准的最高位的0也拿来编码。这个系列包括16个部分,其中 ISO 8859-12已经被放弃,ISO 8859-1得到了最广泛的实现,是替代标准ASCII事实上的标准。然而它缺少欧洲符号,没有完全覆盖芬兰语和法语。ISO 8859-15在ISO 8859-1的基础上增加了这部分修改。ISO 8859-15支持的语言包括:Albanian, Basque, Breton, Catalan, Danish, Dutch, English, Estonian, Faroese, Finnish, French, Frisian, Galician, German, Greenlandic, Icelandic, Irish Gaelic, Italian, Latin, Luxemburgish, Norwegian, Portuguese,Rhaeto-Romanic, Scottish Gaelic, Spanish, and Swedish.

ISO 8859系列标准的完整的字母表如下:
ISO 8859-1 West European languages (Latin-1)
ISO 8859-2 Central and East European languages (Latin-2)
ISO 8859-3 Southeast European and miscellaneous languages (Latin-3)
ISO 8859-4 Scandinavian/Baltic languages (Latin-4)
ISO 8859-5 Latin/Cyrillic
ISO 8859-6 Latin/Arabic
ISO 8859-7 Latin/Greek
ISO 8859-8 Latin/Hebrew
ISO 8859-9 Latin-1 modification for Turkish (Latin-5)
ISO 8859-10 Lappish/Nordic/Eskimo languages (Latin-6)
ISO 8859-11 Latin/Thai
ISO 8859-13 Baltic Rim languages (Latin-7)
ISO 8859-14 Celtic (Latin-8)
ISO 8859-15 West European languages (Latin-9)
ISO 8859-16 Romanian (Latin-10)

4. Unicode编码

wiki上是这么定义Unicode上是这么定义的:统一编码,表示和处理世界上大部分的文字系统的文本文件的计算机工业标准。目前最新的标准是Unicode 9.0.0,包含128,000+的字符,符号,覆盖了135种现代语言,而且此标准还在不断更新中,新的字符和符号会加到标准中。Unicode的成功,主要是由于它统一了字符集,导致在计算机软件国际化和本地化领域得到了广泛的普及和使用。此标准在很多最新的技术中被实现,包括现代的操作系统,XML, Java, .NET等编程语言。

ISO先后也发布了类似的Universal Coded Character Set (UCS) 标准ISO 10646,相同的数字编码在两个标准中表示的符号都是一样的。差异在:

  • Unicode更新的更快,常会有新符号加入
  • Unicode有一些规则和规格是ISO没有的,ISO只是简单的字符映射,相反,Unicode补充了整理和规范化形式的规则,给从右到左书写的语言如阿拉伯语和希伯来语补充了双向算法。对于平台之间的互操作性,特别是使用了双向书写语言的平台,实现ISO可能不够,实现Unicode是必要的。

Unicode定义的代码空间是0x0-0x10FFFF, 代码空间中任意一个值就是一个代码点(code point),Unicode的代码点一般用”U+”加上它的十六进制数字表示,比如 U+0041表示大写字母A的代码点。整个Unicode代码空间被划分成17个等级(plane),标号从Plane0–Plane16, 这17个代码等级被分成基本(Basic)和附加(Supplementary)两大等级,代码点在U+0000–U+FFFF范围内的,属于Plane 0, 等级名叫Basic Multilingual Plane(BMP)。代码点在0x10000 – 0x10FFFF属于Supplementary等级,Supplementary等级又可分成Plane1–Plane16 等16个子等级。每个子等级内的代码点又可分成若干个Block。

每个代码点都有一个通用类别属性。Unicode定义了这些属性:Letter,Mark,Number, Punctuation, Symbol, Separator and Other,每个通用类别属性还可以再细子属性。

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

Unicode标准中特殊的代码点或者字符:

  • high-surrogate/low-surrogate代码点

    high-surrogate, U+D800 – U+DBFF(1024个)。

    low-surrogate, U+DC00 – U+DFFF (1024个)。

    在Unicode中,这2048个代码点本身是非法的,不对应任何字符。它们主要的作用是在UTF-16编码中用来辅助编码 U+10000 – U+10FFFF 这个区间的代码点,具体如何编码见后面的UTF-16编码部分。

  • 66个非字符代码点

    这66个代码点不会用来编码字符,这个集合是固定的,后续也不会增加。集合的代码点包括 U+FDD0 – U+FDEF,以及以FFFE或FFFF结尾的代码点,比如 U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, … U+10FFFE, U+10FFFF。

  • 预留代码点

    这些代码点可以用来编码字符,但是当前标准没有定义。

  • 可私用代码点

    可私用代码点是指可以用来编码字符,但是当前标准没有定义的代码点。应用程序可拿来私用。需要交换信息的应用程序,开发人员需要自己在发送方和接受方协商规范。可私用的代码点有三个区间:

    • Private Use Area: U+E000–U+F8FF (6,400 characters)
    • Supplementary Private Use Area-A: U+F0000–U+FFFFD (65,534 characters)
    • Supplementary Private Use Area-B: U+100000–U+10FFFD (65,534 characters).
  • 图像字符

    Unicode定义的有特定含义的图像字符。

  • 格式字符

    Unicode中格式字符本身是不可见了,但是它的作用是可以影响它旁边的字符的外观和行为。

  • 65个作为控制字符的代码点(U+0000–U+001F 和 U+007F–U+009F)

到目前为止,我们讨论的Unicode代码点,与存储方式是无关的,接下来重点说明Unicode代码点的编码方式。把代码空间中代码点映射到可存储,可传输的一串值,这串值叫做代码值(code value)。

4.1 UTF编码

全称叫Unicode Transformation Format。UTF-8和UTF-16是使用的最多的UTF编码方式,根据Google在2016年11月的统计,现在WWW上87.9%的网页是用UTF-8编码的。Java语言中,char类型用的就是UTF-16。UTF的编码包括UTF-1,UTF-7,UTF-8,UTF-16,UTF-32,详细请点这里,本文只介绍最常用的编码。

4.1.1 UTF-8

一种变长编码,代码单元(Code Unit)的长度是8-bit, 兼容于ASCII。

UTF-8最早提出是在1993年,下图是它的编码方式。从这个表格可以看出,它可以编码的代码点 U+0000 – U+7FFFFFFF。编码的长度从1字节到6字节不等。

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

Unicode在1993年UTF-8版本的基础上,增加了如下约束条件后,发布了2003版的UTF-8,增加这些约束条件是为了匹配UTF-16 编码的约束条件。这些约束条件是:

* 去掉high and low surrogate定义的代码点
* 代码点只编码 0x0000 - U+10FFFF
           
字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

总结UTF-8 2003版的特性:

  • 兼容于ASCII
  • 通过第一字节的序列,可清楚的区分单字节编码和多字节编码
  • 通过第一字节的序列,可清楚的指明编码字节序的长度
  • 前缀特性,第一字节可作为编码序列的开始标记
  • 自同步

    这里需要解释一下,第一字节可作为开始标记,后续的字节以10xxxxxx开始,单字节编码的,以0xxxxxxx, 这些不一样的值,在一定程度上可作为同步信号

  • 相对UTF-16更加节省空间

说了这么多,我们宏观地总结下UTF-8 2003版(当然UTF-16也适用)到底都编码了哪些语言。前128个字符(US-ASCII)需要一个字节。接下来的1920个字符需要两个字节来编码。这涵盖了几乎所有的拉丁字母的剩余部分,希腊语,斯拉夫语,科普特语,希伯来语,阿拉伯语,Syriac语,班巴拉语,以及组合变音符号。BMP里的其它的字符需要三个字节,其中包含中日韩经常使用的几乎所有字符。Unicode中其它等级的字符需要四个字节,包括不常见的中日韩字符,各种古文字,数学符号和表情符号(象形符号)等。

下表给出了几个从Unicode代码点转化为UTF-8编码的实例。

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

许多Windows的应用程序包括记事本,会在文本文件的开头加上“0xEF, 0xBB, 0xBF”表示此文件以UTF-8格式保存。“0xEF, 0xBB, 0xBF”是UTF-8的Byte Order Mark,可简写成UTF-8 BOM。因为UTF-8本身是字节序无关的,而且一些现有的能处理UTF-8的应用程序可能因为增加了UTF-8 BOM而出现问题,所以Unicode的标准既没有强制要求,也没有建议UTF-8使用BOM,但是如果使用的话也是允许的。

4.1.2 UTF-16

代码单元是16-bit,可编码的代码点是 U+0000 – U+D800,U+DFFF – U+10FFFF。编码值由1个或者2个代码单元组成。

1980年后期,有IEEE和Unicode两个组织在研究USC,早期人们以为2^16足以表示世界上的大部分的语言和文字,符号,所以UTF-16早期的设想是使用一个代码单元就能表示世界上的大部分字符。但是,随着字符的增加,特别是中日韩字符的加入,16bit已经不能满足需求。于是Unicode在1996年发布了UTF-16 2.0,把能编码的代码空间从U+10000扩展到了U+10FFFF。国际标准ISO/IEC 10646,以及IETF RFC 2781也发布了类似的标准。

UTF-16 编码细节:

  • 对于代码点属于U+0000 – U+D7FF 和 U+E000 – U+FFFF

    这些代码点都属于BMP,编码值和代码点一一对应,使用1个 16bit 就可以表示了。

  • 对于代码点属于U+10000 – U+10FFFF

    假设某代码点M 的取值范围属于U+10000 – U+10FFFF,

    设N = M - 0x10000, N 的属于[0, 0xFFFFF]。使用20bit即可表示N。

    把N的二进制编码的高10bit 表示记为N2,低10bit表示记为N1,其中N1,N2都属于[0, 0x3FF]。

    那么最终M的代码值可表示为高代码单元C2和低代码单元C1两个代码单元,其中,

    C2 = 0xD800 + N2, C2 属于[0xD800, 0xDBFF]

    C1 = 0xDC00 + N1, C1属于[0xDC00, 0xDFFF]

    也就是说C2刚好属于Unicode的high-surrogate编码区,C1属于Unicode的low-surrogate编码区。

    high-surrogate/low-surrogate编码区的代码点在Unicode代码空间是非法的,换句话说,正常字符的编码是不会出现在high-surrogate/low-surrogate编码区的,这样就保证了UTF-16整个代码值的唯一性。

下表给出了几个从Unicode代码点转化为UTF-16编码的实例。

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

4.1.3 UTF-32

UTF-32 是一个定长的编码方法,就是使用4字节来直接表示Unicode代码点。这样的好处是代码点和代码值是直接对应的,它们间的相互转化非常高效,缺点也明显,就是浪费空间,因为文本中大部分字符都在BMP内,所以存储空间基本是UTF-16的2倍,如果文本文件都是ASCII字符的话,是UTF-8的约4倍。

4.1.4 UCS-2

从名字就可以看出,UCS-2,UCS-4标准最早是由ISO提出的。UCS-2直接使用2字节来编码BMP内的代码点。由于UCS-2无法编码代码点超过BMP的字符,所以它的使用非常少。Unicode基于它进行了改进得到了UTF-16。

4.1.5 UCS-4

UCS-4直接使用4字节的定长编码方式。最早UCS-4 的编码空间是0x0 – 0x7FFFFFFF。因为 ISO/IEC JTC 1/SC 2工作组2 规定,未来所有字符的分配都应该加上Unicode代码点范围的限制(即U+0000 – U+D800,U+DFFF – U+10FFFF)。所以UTF-32和UCS-4是基本一致的。

4.1.6 UTF-16和UCS-2 BOM问题

由于UTF-16和UCS-2编码产生的序列是一串16bit代码单元,因为大部分通信和存储协议是以字节为单位的,一个UTF-16代码单元有两个字节,这两个字节怎么放就与字节序有关了。为了更好地说明这个问题,我在Windows平台存储了一个文本,里面放在ABCD的字符串,然后分别存储为ASCII, UTF-16LE, UTF-16BE三个格式,然后在Ubuntu上使用hexdump -b ABCD_ASCII.txt查看字节序列,注意是八进制的。可以发现:

  • ASCII文件刚好是四个字母的ASCII,占用4个字节。UTF-16的文件的前面都多了2个字节,UTF-16LE文件前面多了0xFFFE,UTF-16BE文件前面多了0xFEFF。
  • 小端LE和大端BE中每个代码单元的字节序刚好相反。
    字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

为了帮助识别代码单元的字节序,UTF-16 允许在文件的开头插入一个代码点U+FEFF(U+FEFF 是一个zero-width non-breaking space/ZWNBS字符)作为BOM。当解码器的字节序和编码器的字节序一致的情况下,它将识别到0xFEFF,当不一致时,它识别到的是0xFFFE, 这样解码器处理后面的代码单元时就需要交换字节。Windows平台一般都使用了BOM, 类Unix系统一般不使用BOM。如果没有BOM, 有一个方法可帮助识别字节序,就是查找特别常用的空格字符(U+0020),看他的字节就知道编码器的字节序了。

5. 中文编码

5.1 GB2312

GB2312(1980)是中华人民共和国简体中文编码字符集的官方注册网名。GB是GuojiaBiaozhun的缩写。GB2312已经被GBK和GB18030标准取代了,据统计,2010年1月有3.5%的网站使用GB2312编码,但是到了2016年4月,这个数字降到了0.8%。

GB2312的代码点用94*94的表格表示,行号代表区号,编号从1到94,使用第一个字节表示,列号代表区号中的位号,编号从1到94,使用第二个字节表示,所以GB2312的代码点也叫区位码。

其中区号是这么划分的:

  • 01-09:由标点符号,特殊字符,以及Hiragana, Katakana, Greek, Cyrillic, Pinyin, Bopomofo组成
  • 10-15:预留
  • 16-55:汉字的第一个等级(plane),共3755个字符,根据拼音排列。
  • 56-87:汉字的第二个等级(plane),共3008个字符,根据笔画排列。
  • 88-89:其它103个汉字,留给GB/T 12345标准使用
  • 90-94:预留

从区号划分可知,GB2312共编码了6763个汉字,以及一些其它字符。

在计算机程序里,EUC-CN是编码GB2321常用的字符编码方式。它是一个2个字节编码方式,第一个字节从0xA1-0xF7 (161-247),第二个字节从0xA1-0xFE (161-254),因为2个字节的取值范围都不在[0, 127], 所以EUC-CN兼容于ASCII。

根据上面的定义,可以得出从代码点到编码值的计算公式:

假设代码点为P,代码值的第一,第二个字节分别是V1,V2,

那么,V2 = 158 + P / 94,V1 = 158 + P % 94。

举个栗子,汉字”外” 的代码点是4566,那么高字节为0xCE=4566/94+158,

低字节为0xD4=4566%94+158,汉字“外”的完整编码是0xCED4=52948。

HZ是另外一个编码GB2312的编码方式,常用在email和新闻信息编码。

5.2 GBK

1993年,Unicode 1.1发布,此标准包括20902个在中国大陆,台湾,日本,韩国使用的字符。接着,国家信息产业部电子工业标准化研究所发布了与Unicode 1.1相同的GB13000.1:1-93标准。

1993年,GBK标准发布。GBK作为GB2312-80的一个扩展,除了GB2312-80定义的字符外,同时也包括GB13000.1:1-93定义的字符和GB2312未使用的代码点。因此GBK兼容于GB2312。GBK是“GuojiaBiaozhunKuozhan”的缩写。据统计,截止2016年1月,互联网上有0.3%的网页使用GBK。Microsoft 在Windows 95和 Windows NT 3.51实现了GBK,尽管GBK不是官方标准,但是由于Windows 95的广泛使用导致GBK成了事实上的标准。

1995年,中国国家信息技术标准化技术委员会制定的汉字内码扩展规范1.0,也就是GBK1.0,它是基于GBK进行简单的扩展,新增加的95个字符是GB13000.1:1-93未包含的,这些代码在Unicode中属于预留代码点。

GBK字符要么编码成1个字节,要么是2个字节。编码是1个字节时,就是US-ASCII定义的127个字符。如果某字节的最高位为1,那么它是2个字节编码。下表是GBK定义的代码值。

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

下图是根据GBK代码值划分区域后的更加直观的图表

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

5.3 GB18030

GB18030-2005字符集的全称是”中国国家标准GB18030-2005:信息技术–汉字编码字符集”,此标准除了定义了GB18030的编码规范,还规定了在中国使用的软件必须支持的字符和字体等。GB18030支持Unicode,包含简体和繁体字符,兼容于GB2312, CP936, 和 GBK 1.0。

GB18030是一个1/2/4个字节(UTF)的变长编码。1个字节编码的是US-ASCII(线性的),2个字节编码的是GBK定义的字符(非线性映射),4个字节映射到Unicode中字符(线性的)。具体映射关系如下表。

字符编码初探1. 字符编码术语2. ASCII3. ISO 8859标准系列4. Unicode编码5. 中文编码6. 参考文献

5.4 Big5

Big5是通行于台湾,香港,澳门的繁体字符编码集。此标准是由5家公司共同研发的标准,由于缺少很多常用的字符,所以有很多公司对它进行了扩展。Big5是一个双字节编码,第一个字节从0x81到0xfe,第二个字节从0x40到0x7e和从0xa1到0xfe。

BIG5的编码可划分为不同的区域:

0x8140 0xa0fe 预留给用户自定义
0xa140 0xa3bf 图像字符
0xa3c0 0xa3fe 预留,用户不能自定义字符
0xa440 0xc67e 常用字
0xc6a1 0xc8fe 预留给用户自定义
0xc940 0xf9d5 次常用字
0xf9d6 0xfefe 预留给用户自定义

6. 参考文献

  1. 几乎所有编码的代码页合集: https://en.wikipedia.org/wiki/Code_page_936
  2. 程序员趣味读物:谈谈Unicode编码: http://pcedu.pconline.com.cn/empolder/gj/other/0505/616631_all.html#content_page_1
  3. 字符集编码与 C/C++ 源文件字符编译乱弹:[http://jimmee.iteye.com/blog/2165685
  4. 中文的几个编码 GB2312、GBK、GB18030、GB13000、BIG5: http://blog.csdn.net/blade2001/article/details/4390580
  5. 真正的百科全书,专业,全面: https://en.wikipedia.org/wiki/Wiki
  6. Unicode术语表: http://www.unicode.org/glossary/
  7. UTF-8, a transformation format of ISO 10646: http://www.ietf.org/rfc/rfc3629.txt

继续阅读