1. 概念
- 字符:是各种文字和符号的总称,包括各国家的文字、标点符号、图形符号、数字等
- 字符集:多个字符的集合,常见的有ASCII,GBxx,Unicode等
- 字符编码:是把字符集中的字符编码为特定的二进制数,以便计算机存储。字符集中的每个字符编码都不同。
2. 字符集
2.1 ASCII维基百科:(发音: /ˈæski/ ass-kee[1],American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本EASCII则可以部分支持其他西欧语言,并等同于国际标准ISO/IEC 646。
ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(一些终端提供了扩展,使得这些字符可显示为诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符。用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。
Unicode
维基百科:Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。
Unicode伴随着通用字符集的标准而发展,同时也以书本的形式[1]对外发表。Unicode至今仍在不断增修,每个新版本都加入更多新的字符。目前最新的版本为2017年6月20日公布的10.0.0[2],已经收录超过十万个字符(第十万个字符在2005年获采纳)。Unicode涵盖的数据除了视觉上的字形、编码方法、标准的字符编码外,还包含了字符特性,如大小写字母。
Unicode发展由非营利机构统一码联盟负责,该机构致力于让Unicode方案取代既有的字符编码方案。因为既有的方案往往空间非常有限,亦不适用于多语环境。
Unicode备受认可,并广泛地应用于电脑软件的国际化与本地化过程。有很多新科技,如可扩展置标语言(Extensible Markup Language,简称:XML)、Java编程语言以及现代的操作系统,都采用Unicode编码。
3. 字符编码
Unicode字符集虽然定义了每一个字符的二进制代码,但是没有定义这些二进制怎么存储(用多少个字节存储)。而其他大多数字符集本身也是字符编码,比如ASCII使用一个字节存储一个字符,GB2312用两个字节表示一个字符。
【问题】unicode能够表示世界上任何一个字符,所以一个字符的二进制位数也相应增加,但是如ASCII一个字节表示的现代英语字符,如果用三个字节或更多的字节去表示就会造成浪费。所以它造成的结果是,有许多不同的二进制格式用来表示Unicode.
3.1 UTF-8
UTF-8是使用最广的一种unicode实现方式。它是一种变长的编码方式,它可以使用1~4个字节表示一个符号,根据不同的符号变化字节长度。
UTF-8的编码方式
这里我觉得阮一峰的博客讲的比维基百科更易理解,所以放上它的链接。
上图的根据
写错了。2333.
UTF-16
UTF-16的编码方式较UTF-8更为复杂。
通过查阅wiki,大致明白了UTF-16的编码方式。
Unicode的编码空间从U+0000到U+10FFFF,共有1,112,064个码位(code point)可用来映射字符. Unicode的编码空间可以划分为17个平面(plane),每个平面包含216(65,536)个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。
- 第一个平面称为基本多语言平面
- 其他平面称为辅助平面
基本多语言平面内,从U+D800到U+DFFF之间的码位区块永久保留不映射到任何unicode字符。UTF-16就利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。
- 从U+0000至U+D7FF以及从U+E000至U+FFFF的码位
正常表示对应的字符 - 从U+10000到U+10FFFF的码位
辅助平面中的码位被编码为一对(两个)16比特长的码元,称作代理对(在guava Strings类源码中,我曾被这个代理对弄得很晕)。
具体计算方法:- 码位减去0x10000,得到的值的范围为20比特长的0..0xFFFFF.
- 高位的10比特的值(值的范围为0..0x3FF)被加上0xD800得到第一个码元或称作高位代理(high surrogate),值的范围是0xD800..0xDBFF.由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode标准现在称高位代理为前导代理(lead surrogates)。
- 低位的10比特的值(值的范围也是0..0x3FF)被加上0xDC00得到第二个码元或称作低位代理(low surrogate),现在值的范围是0xDC00..0xDFFF.由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode标准现在称低位代理为后尾代理(trail surrogates)。
上述算法可理解为:辅助平面中的码位从U+10000到U+10FFFF,共计FFFFF个,即220=1,048,576个,需要20位来表示。如果用两个16位长的整数组成的序列来表示,第一个整数(称为前导代理)要容纳上述20位的前10位,第二个整数(称为后尾代理)容纳上述20位的后10位。还要能根据16位整数的值直接判明属于前导整数代理的值的范围(210=1024),还是后尾整数代理的值的范围(也是210=1024)。因此,需要在基本多语言平面中保留不对应于Unicode字符的2048个码位,就足以容纳前导代理与后尾代理所需要的编码空间。这对于基本多语言平面总计65536个码位来说,仅占3.125%.
2048个码位
刚好就是U+D800
到U+DFFF
直接的码位,也就是说用两个16位的处于U+D800
到U+DFFF
之间的数来表示从U+10000到U+10FFFF的码位。
由于前导代理、后尾代理、BMP中的有效字符的码位,三者互不重叠,搜索是简单的:一个字符编码的一部分不可能与另一个字符编码的不同部分相重叠。这意味着UTF-16是自同步(self-synchronizing):可以通过仅检查一个码元就可以判定给定字符的下一个字符的起始码元. UTF-8也有类似优点,但许多早期的编码模式就不是这样,必须从头开始分析文本才能确定不同字符的码元的边界。
从上面的解释可以明白,只需要判断当前16位比特值在哪个区间就可以判断是用一个16位能够表示还是需要两个16位来表示一个字符。即UTF-16是可变长的。
4. Little endian 和 Big endian
同样引用阮一峰博客原文
:
上一节已经提到,UCS-2 格式可以存储 Unicode 码(码点不超过0xFFFF)。以汉字严为例,Unicode 码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,这就是 Big endian 方式;25在前,4E在后,这是 Little endian 方式。
这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-endian)敲开还是从小头(Little-endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
第一个字节在前,就是”大头方式”(Big endian),第二个字节在前就是”小头方式”(Little endian)。
那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode 规范定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格”(zero width no-break space),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
参考文献
[1] 字符编码笔记:ASCII,Unicode 和 UTF-8
[2] Unicode
[3] UTF-16