字符集、字符编码编码总结:ANSI、UNICODE、MBCS、ASCII等等
目录
一、字符集与字符编码
二、字符集的发展
1. 单字节字符集(SBCS)
2. 多字节字符集(MBCS)
3. 宽字节字符集(Unicode)
三、UTF - Unicode/UCS Transformation Format
1. UTF-8
2. UTF-16
3. UTF-32
四、代码页(Code Page)
五、Unicode编程
一、字符集与字符编码
字符集(英文名:Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。
计算机要准确的处理各种字符集文字,就需要进行字符编码(英文名:Character encoding),以便计算机能够识别和存储各种文字。字符编码也称字集码,是把字符集中的字符编码为指定集合中某一对象(例如:比特模式、自然数序列、8位组或者电脉冲),以便文本在计算机中存储和通过通信网络的传递。常见的编码方式有:ASCII、GB2312、BIG5、Unicode(UCS-2,UCS-4)、UTF-8和BASE64等等。
简单说:
- 字符集:为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point)
- 字符编码:将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)
例如:Unicode 是「字符集」,UTF-8 是「字符编码」。
在Unicode出现以前,字符集与字符编码是没有区分的。例如ASCII、GB2312、GBK、BIG5等等标准即是字符集,也是字符编码。
二、字符集的发展
计算机字符集可归类为三种,单字节字符集(SBCS)、多字节字符集(MBCS)和宽字符集(Unicode字符集)。
1. 单字节字符集(SBCS)
单字节字符集,称之为SBCS,它的所有字符都只有一个字节的长度。常见字符集有:ASCII码和扩展ASCII码。SBCS字符串由一个零字节结尾,数据类型是char。
ASCII码:目前计算机中用得最广泛的单字节字符集及其编码,是由美国国家标准局(ANSI)制定的ASCII码(American Standard Code for Information Interchange,美国标准信息交换码),它已被国际标准化组织(ISO)定为国际标准,称为ISO 646标准。ASCII码适用于所有拉丁文字,它用7位二进制数进行编码(其最高位(bit7)被用做奇偶校验位),可以表示128个字符。
第0~32号及第127号(共34个)是控制字符或通信专用字符,如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BEL(振铃)等。
第33~126号(共94个)是字符,其中第48~57号为0~9 10个阿拉伯数字;65~90号为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
参考:ASCII码 - 基本的ASCII码和扩展的ASCII码,最全的ASCII码对照表
2. 多字节字符集(MBCS)
当非拉丁语国家人民使用计算机时,需要将他们自己使用的文字(字符)进行编码。此时ASCII码占用1个字节预留的空间(128~255)就不够用了。因此,非拉丁语国家对自己的文字进行编码时,通常采用在ASCII基础上扩展,并用多个字节表示一个字符。这种字符集就叫多字节字符集。同SBCS一样,MBCS字符串也由一个零字节结尾,数据类型也是char。
例如聪明的中国人民为了解决汉子编码时想到了这样的办法。我们不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都重新编了两个字节长的编码,这就是常说的“全角”字符,而原来在127号以下的那些就叫“半角”字符了。中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312”。GB2312 是对 ASCII 的中文扩展。但是中国的汉字太多了,我们很快就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把GB2312 没有用到的码位找出来老实不客气地用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。
类似的,其他的国家和地区也在ASCII码基础上扩展出自己的字符集。例如:BIG5(台湾)、JIS(日本)。这些扩展得来的多字节字符集彼此并不兼容,会将大于127的码点(Code Point)定义成自己的字符。因此,采用MBCS设计的程序、网页在不同地区解码时就可能出现乱码!
3. 宽字节字符集(Unicode)
由于MBCS是各个国家和地区为本地文字进行编码的标准方案,彼此互不相通,不同的语言字符编码值相同却代表不同的符号(例如:韩文编码EUC-KR中“한국어”的编码值正好是汉字编码GBK中的“茄惫绢”)。因此,同一份文档,拷贝至不同语言的机器,就可能成了乱码。为了解决这一问题,人们设想定义一个超大字符集,能容纳全世界所有已知字符、文字,并进行统一编码,于是设计了通用字符集。
如果说“各个国家都在为自己文字独立编码”是百家争鸣,那么“建立世界统一的字符编码”则是一统江湖,谁都想来做这个武林盟主。早前就有两个机构试图来做这个事:
- 国际标准化组织(ISO),他们于1984年创建ISO/IEC JTC1/SC2/WG2工作组,试图制定一份“通用字符集”(Universal Character Set,简称UCS),并最终制定了ISO 10646标准。
- 统一码联盟,他们由Xerox、Apple等软件制造商于1988年组成,并且开发了Unicode标准(The Unicode Standard)。
1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都独立存在,并独立地公布各自的标准。不过由于Unicode这一名字比较好记,因而它使用更为广泛。
Unicode编码点分为17个平面(plane),每个平面包含(即65536)个码位(code point)。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。
其中第0号平面又叫基本多文种平面。基本多文种平面,BMP(Basic Multilingual Plane),或称第零平面(Plane 0),是Unicode中的一个编码区段。编码从U+0000至U+FFFF。 Unicode 基本多文种平面的示意图如下。每个写着数字的格子代表256个码点。(CJK是中日韩文字区域,这个区域很大哦!)
三、UTF - Unicode/UCS Transformation Format
早期字符集与字符编码是不区分的,例如ASCII编码、GB2312编码、GBK编码等等。这些标准直接取字符的码点(Code Point)进行16进制编码即可以完成编码。
当Unicode出现时,由于其定义了多个平面,而最常用的又是基本多文本平面。因此,最开始的编码方式是UCS-2,即读基本多文种平面的65536个字符进行2个字节的16进制编码。这种编码的缺点是:
- 不兼容ASCII码。所有ASCII字符都需要加上0x00的字节,极大地浪费了存储空间。因此,后续发展了UTF-8编码。
- 不支持辅助平面字符码点(surrogate code points)。后续发展了UTF-8、UTF-16、UCS-4、UTF-32等等。
其中UTF-16对基本多文种平面的编码与UCS-2是相同的,因此UTF-16可以视为UCS-2的父集。UTF-32 原本是 UCS-4 的子集,但JTC1/SC2/WG2声明,所有未来对字符的指定都将会限制在BMP及其14个补充平面。于是就现状而言,除了 UTF-32 标准包含额外的 Unicode 意涵,UCS-4 和 UTF-32 大体是相同的。
UTF是Unicode Transformation Format的缩写,即Unicode字符集转换格式。目前最常用的是UTF-8和UTF-16。其中UTF-8在网页、移动端使用最多。
1. UTF-8
UTF-8采用变长字节对字符进行1-4个字节的编码,具体取决于码点数值中有效位的数量。
UTF-8编码中,前128个字符(US-ASCII)需要一个字节。接下来的1,920个字符需要两个字节进行编码,其中涵盖了几乎所有拉丁字母字母的其余部分,还包括希腊语,西里尔语,科普特语,亚美尼亚语,希伯来语,阿拉伯语,叙利亚语,塔那那语和N'Ko字母以及组合变音词马克。剩余基本多语言平面中的字符需要三个字节,其中几乎包含所有常用字符,包括大多数字符中文,日文和韩文字符。Unicode的其他平面中的字符需要四个字节,其中包括不常见的CJK字符,各种历史脚本,数学符号和表情符号(象形符号)。其最大的优势是与标准ASCII码完全兼容,对ASCII字符编码只采用1个字节编码。在网页端传输因为有大量英文字符,采用UTF-8编码能极大地减少存储空间。但是UTF-8对汉字并不“友好”,大部分汉字都需要占用3个字节存储。这也是为什么大部分中文网页或者程序依然会采用GB2312、GBK编码的原因。
以下为UTF-8对Unicode中文字符编码的一个示例:
根据上表中的编码规则,字符「宇」的Unicode码点为 U+5B87 属于第三行的范围:
|
这就是将 U+5B87 按照 UTF-8 编码为字节序列 E5AE87 的过程。
2. UTF-16
UTF-16对基本多文种平面字符进行两个字节编码,并且依赖字节序(Byte Order Mark,简称BOM)。对于非基本文种平面的辅助平面字符,采用四个字节编码。
UTF-16对基本多文种平面的字符进行编码时,编码值直接取字符的Unicode码点值。这样绝大多数语言的字符都能利用两个字节进行编码。除基本多文种平面外,还剩0xFFFFF个码位(并且其值都大于或等于0x10000)。对于“辅助平面”内的字符来说,如果用它们在Unicode中码点值减去0x10000,则可以得到一个0~0xFFFFF的区间(该区间中的任意值都可以用一个20-bits的数字表示)。UTF-16 将这 20-bits分成两半,前 10 位映射在 0xD800 到 0xDBFF,称为高位(H),后 10 位映射在 0xDC00 到 0xDFFF,称为低位(L)。即该数字的前10位(bits)加上0xD800,就得到UTF-16四字节编码中的前两个字节;该数字的后10位(bits)加上0xDC00,就得到UTF-16四字节编码中的后两个字节。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。因此,当我们遇到两个字节,发现它的码点在0xD800 到 0xDBFF 之间,就可以断定,紧跟在后面的两个字节的码点,应该在 U+DC00 到 U+DFFF 之间,这四个字节必须放在一起解读。
注意,0xD800到0xDFFF的区间恰好位于基本多文种平面的UTF-16 surrogoates区域。
以下为UTF-16对Unicode中文字符编码的示例: