- ASCII 编码
全称为’American Standard Code for Information Interchange’. 是由美国标准委员会(American Standards Association) 为了解决字符在计算机中的存储于 1963年提出的一种字符编码方式, 在1967年经过较大修正, 1986年变为现今广泛使用的版 本,其广泛用于计算机,以及其他设备中文本的存储。其他不同的字符编码方式(UTF-8, UTF-18, UCS2 等) 都是基于ASCII编码,在2007年被UTF-8 超越之前,是世界上最广泛使用的编码方式。
用法:
此种编码使用一个字节(8 bit)表示任何英文数字字符(A-Z, a-z),因此可以表示的字符集为128(0x00 - 0x7F), 最高位始终为0, 其中 33位为不可打印的控制字符(许多现已经废除, 其中前32位(0x00 – 0x30) 包括 响铃(\a), 水平制表符(\t), 换行符(\n), 回车符(\r),
注: 换行符’line feed’ 与回车符’carriage return’的区别:
换行符’\n’表示开始新的一行, 回车符表示’\r’ 将光标移动到一行的开始。在不同的操作系统中对换到新一行有不同的表示。
-
-
- Windows:使用换行+ 回车表示新的一行 (\r\n)
- Unix/Linux: 使用换行表示新的一行 (\n)
- Mac OS: 使用回车 表示新的一行 (\r)。
-
因此对于此直接的问题就是,当你用Windows 记事本编辑了一段文档后,在Mac或者 Unix中打开的时候 每行结尾将会多出一个’^M’ 符号,而在其他系统中编辑的文档在Windows中显示的时候则会变成连续的一行。
0x7F为删除符 (DEL) , 另外剩下95位为各种可打印字符,包括大小写英文字母,数字,特殊符号等, 比如0x41代表 英文字母A,0X61代表小写字母a。 附录为ASCII 码表
2. Unicode 编码
ASCII编码使用一个字节中的后7位,原先大多数电脑为1个字节字长,因此除了128位ASCII字符外,可以利用最高位表示其他的字符编码,但是不同国家和地区, 以后不同的组织 对于如何利用剩下的字节空间 有不同的编码规范,对于某些国家,字符代码130 表示é, 但是在以色列 ,130 表示希伯来文符号 () , 而在俄罗斯,对于另外空余的128 位字符表示 又有自己的表示方法。
对于高位128个字符如何表示,对此需要提出一个叫做’Code Page’(代码页)的概念, 因代码页概念过于模糊,本身无法表达真正的意思,因此后均用简写CP表示。
在1980-1990年间,在微软Windows系统中的使用的CP主要包括两大类型 ——ANSI Code Page和 OEM Code Page, 它们由不同的地公司提出,但是都是作为基本的ASCII编码的扩展,定义了高位128个映射。
ANSI Code Page:
由微软提出的一套CP的合称,第一个ANSI 1252 基于 ISO 8859-1标准,从0x80-0x9F定义了一些额外的可打印字符。 以下是一些提出的CP , 以数字区分
1254 – 土耳其, 1256 – 阿拉伯语, 1255 – 希伯来语,1258 越南语。
从以上可见,对于不同的语言,定义了不同的CP, 在Unicode未成为正式标准之前,此种解决方案,可以使得Windows系统能够通用于不同的国家和地区。
OEM Code Page:
此CP是由不同的OEMs厂商提出的,主要针对MSDOS系统的一套解决方案,并非由微软或者标准组织提出,因此被统称为OEM Code Page. 此种CP最初主要被封装在IBM 个人电脑的显卡中,包括MDA, CGA以及后来的VGA显卡,这些显卡的接口使 用一个字节来表示每种不同的字体和编码。下面是一种不同的OEM CP的类型。
CP 437 是在IBM 个人电脑 以及 MS-DOS上使用的一套OEM字符集,这种字符集除了定义32-126的通用ASCII编码外,额外扩展了一些希腊字母符号。 因此有可能在不同的地区,其封装的CP就不一样.
因为437 并不能作为国际通用的编码标准,因此以CP 437为基础,对于一些额外的国家的地区,扩展出一系列不同的CP,例如以色列地区的码表代号为862, 而希腊地区的码表为737。 所以如果一个系统要能够表示不同地方的符号代码,它需要预装这些 不同地区的码表,尽管低128位符号的表示都是一样的。
Ok, 问题解决了,但是,利用此种码表,如果你要在计算机上同时显示希腊语和希伯来语,该如何解决 ? 高128位 相同的数字代表会完全不同的符号。 你需要自己写程序告诉计算机该使用何种码表来显示不同的语言符号。
对于东方国家,例如中国,日本或者韩国,使用的字符已经远远超过了1个字节所能表示的, 光汉语本身,常用的汉字就几千个。因此对于东方国家,提出了自己本身的编码解决方案,比如中国的GB2312(中华人民共和国国家标准简体中文字符集),收录了 六千多汉字,使用2个字节表示单个汉字符号,因此可以表示的汉字符号总共可为2^16 (65536), 因此也作为DBCS(double-byte character sets ), 足够表示日常使用的所有汉字符号。 而台湾地区使用Big 5编码,作为繁体中文的编码标准,同样也是 使用双字节编码标准。
随着因特网的普及,以及不同国家和地区日益频繁的交流,迫切需要一套全球统一的编码标准,对于每个国家地区的不同符号有一个唯一的码值,因此Unicode协会创作出了一套通用的字符集 Unicode. 在Unicode 中, 每一个字符都有一个对应的码点 (Code Point), 注意此只是概念上的表示,对于每个具体符号在内存、磁盘中的表示有不同的编码机制。例如英文字母A 的码点为 U+0639, 阿拉伯字母Ain 码点为U+0639。 因此对于一个英文单词 ‘Hello’, 其Unicode表示为
Hello
U+0048 U+0065 U+006C U+006C U+006F.
注意 :
-
- Unicode中的每个符号代码不一定为2个字节表示,2个字节的代码最多只可以表示 65536个字符。事实上Unicode的字符数已经超过12万个字符, Unicode的码点数也已经超过100万,范围从0x000000 到 0x10FFFF。 Windows 系统下 可以输入’charmap’ 运行’Character Map’ 程序,查看每个字符对应的Unicode 码点。
- Unicode中的码点与字符并不是一一对应的关系,许多码点对应于某个字符,还有额外其他的码点规定了一些其他的例如控制、格式化的命令符号。
- Unicode 只是定义了某种字符集,规定了从某种字符到相应码点的映射。而UCS和UTF系列则提供了具体的字符编码解决方案。
字符平面 (Code points Planes)
Unicode将此100多万个码点分成了17个平面,以数字0到16表示。其中第0平面 (0x0000 – 0xFFFF)也称为基本多文种平面 (BMP) 使用4位16进制数,其余平面使用 5到6个 16进制数。 下图为Unicode的平面表
其中BMP平面 在UTF-16编码中使用一个码值,剩余的1-16平面使用代理对(surrogate pair)表示
以Unicode的码值为基础,依据每个码点对应到不同的比特序列, 有2种大致的编码方式 —— UTF编码 和 UCS编码,其中UTF系列 的数字代表码点对应的最低编码位数, 对于UCS编码,数字表示的每个码值的比特数。
3. UTF-8编码
此种编码方式是万维网中最常用的一种变长编码方式,其中超过8成的网页使用此种编码方式, 它将Unicode中的每个码点,编码对应为1-4位比特长度。其中低码点的Unicode, 使用1,2位比特编码,对于高码点的Unicode, 则使用3,4位比特进行编码,通过此 种变长编码方式,可以有效的减少文本的编码长度,节约存储空间,方便与进行网络间传输。
当通过浏览器请求某一网页时,最常见的网页头 表示
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
其中 content规定了此网页的字符集编码标准。因此当浏览器收到content头的时候,则会使用相应的编码解析网页。
为了与前文介绍的ASCII码 兼容,对于UTF8 编码 前128位字符与ASCII码相同,此种方法最大的好处就是ASCII编码的文件 同时可以以UTF8格式读取。在桌面上创建一个新的文本文档, 当另存为的时候,使用2种不同的编码方式存储, ASCII和UTF8,使用2 进制打开文本文档的时候,2种文本文件的码值是一样的。
编码方式:
对于Unicode中的100多万个码点,在UTF8种使用1到4个字节编码,其中编码方式如下表所示:
解释如下
-
- 前128位码点(U+0000 – U+007F ),其编码与ASCII表示相同, 使用1个字节表示
- 从U+0080 – U+07FF, 其编码使用2个字节表示, 高位字节二进制为110xxxxx, 低位字节为10xxxxxx,任意取出某一码点的二进制表示,从后往前一次往’x’中填充,多余的补0
- 从U+0800-U+FFFF, 使用3个字节表示, 高位字节1110xxxx, 后两位为10xxxxxx, 10xxxxxx, 余下填充方法与2位类似
注:
- 图中‘Byte 1’中的1的个数表示编码的字节数,因此从第一个字节的数可以直接推出编码的字节数。
- UTF8最多字节数为4, 2003年根据RFC3629 规范,UTF-8的表示范围截至到U+10FFFF。 因此UTF-8最高可以表示的字节数为4个。
UTF-8的优缺点:
-
- UTF8的前128位与ASCII一样,意味着支持ASCII编码的软件同样可以处理UTF8编码。
- UTF8可以编码Unicode字符中的任何一个码字,当需要显示不同的语言符号时,不再需要设置不同的码页。
- UTF8是Xml文档的唯一编码规范。
- 相比于UTF16 , UTF8编码流不需要UTF 16的BOM 来规定额外的的字节顺序。
- UTF8 是一种自同步的编码方式,由于UTF8编码可以通过第一个字节高位1的个数来确定每个编码的字节数。因此当传输过程发生错位,可以很容易的定位错位,找到下一个字符的编码起点。
- 但是作为一种囊括所有Unicode字符的编码规范,对于某些语言,编码同样的文档,UTF8 将会耗费更多的字符空间。
大端与小端
大端与小端描述的是在计算机内存中字节的两种不同的存储顺序,或者是传输过程中的字节的不同顺序。大端将一个字长中最高字节存储于某一个内存空间,余下的字节存储在高位的内存空间。而小端 则正好相反,高字节存储于高位内存空间, 低位字节存储于 低位内存空间。在设计的过程中,大端与小端的选择是随意的,例如Intel x86处理器 使用小端,而IBM的 大型机架构(mainframe architecture) 使用大端。
大端是网络交换中最经常使用的方式, TCP,UDP,IpV4均使用大端方式进行数据传输,因此大端的字节顺序也称为网络字节顺序,而小端方式由于Intel公司在计算机芯片领域的垄断地位,因此在微处理器上经常使用此种方式。以下为大端和小端的示意图:
4. UTF-16:
UTF-16是在UCS-2的基础上发展起来的一种变长编码方式,相比于UCS-2使用2个字节编码,此种方式使用2-4个字节对Unicode的100多万个码点进行编码。此种方式的基本目标为对于1比特的编码方式,扩展至2比特,进行表示UCS中的所有字符, IEEE和Unicode 委员会同时进行工作,因此此种编码方式为称为UCS-2。
但是很快,大家意思到光用2个比特表示所有的字符是不够的,因此iEEE提出使用4个比特表示一个字符,但是遭到Unicode委员会的反对, 不仅是因为固定4个字节编码对于会浪费大量的额外存储空间,而且某些公司已经开始研发支持UCS2编码的设备。 因此提出了一种额外的折中方案 —— UTF-16。
编码方式:
U+0000 – U+FFFF
UCS2与 UTF-16的编码方式是一样的,都是Unicode码点中相应的数字表示。对于其他的辅助平面,UTF-16则使用一对16位的二进制编码表示。
U+10000 – U+10FFFF
-
-
- 使用Unicode的码点减去U+10000, 剩余(0x0000 – 0x00FFFF)
- 剩下的高10位(0x0000 – 0x03FF) 与 0xD800相加, 变高位 的16位二进制表示0xD800 – 0xDBFF.
- 低10位(0x0000 – 0x03FF) 与0xDC00相加, 变成低位的16位二进制表示0xDC00 – 0xDFFF
-
U+D8FF – U+DFFF
保留
字节序 Byte Order Encoding (BOM):
相应于前文介绍的大端与小端,在UTF -16中 对应的大端与小端表示,分别表示为 UTF-16 BE和 UTF-16 LE.
为了区分在一段字节流中,具体使用的时大端还是小端,因此在UTF-16编码流中,使用BOM进行区分。 BOM是加在流开头的2个字节表示,U+FEFF (zero-width non-breaking space), 如果头2个字节为FEFF, 则为大端方式,反之,则使小端。如果当显式指 出使用UTF-16LE或者UTF-16BE时,则BOM并没有什么用处。
5. 不同的编码方式比较 (ASCII / UTF -8 / UTF -16 BE / UTF – 16LE)
以下的对比是在设置系统的区域语言为简体中文时的对比,如果为其他系统语言,ANSI格式的编码字节会有所不错
在Windows中使用记事本,当另存为,有4种不同的编码方式 (ANSI, Unicode, Unicode Big Endian, UTF-8),这4种不同的方式列表如下
-
- ANSI: 对应当前系统 locale的遗留编码。默认语言的修改可以通过控制面板的时钟语言和区域选项进行设置。比如当设置为英文时候,则使用ASCII编码,中文简体,使用GB2312, 而中文繁体使用Big 5编码。
-
- Unicode: 加上BOM 的UTF-16 小端序的编码,
- Unicode (Big Endian): 加上BOM的UTF-16 大端序的编码
- UTF-8: 加上BOM的 UTF-8 的编码。
以下为在Windows系统下,创建一个文本文档, 输入汉字’编码’, 存储为不同的编码格式 , ANSI, UTF 16-LE, UTF 16-BE, UTF 8.
ANSI
其中 B1E0 为’编’的Gb2312码值,C2EB为’码’的GB2312码值
UTF-8
在Windows系统中当文本文件存储为UTF-8格式时,系统自动在文件开头添加三个字节’EF BB BF’,剩下的汉字‘编’和‘码’在UTF-8种的16进制表示分别为’0xE7BC96’和’0xE7A081’
UTF-16 LE
上图为Windows系统中文档的UTF16小端编码,系统在开头加上两个字节’ 0xFFFE’ 以表示为小端序, 剩下的’0x167F’和’0x0178’分别代表‘编’和‘码’的UTF-16编码。
UTF-16 BE
类似于小端UTF16编码,上图开头BOM为’0xFEFF’, ‘编’和‘码’各自UTF-16编码的2个字节调换顺序。
6. UCS 2 / UCS4
因为UCS2 于 90年代由 ISO 10646 提出,现在已经由 UTF系列代替,因此只是简单介绍UCS的编码方式。
UCS2 对于UCs中的每一个码值 使用2个比特的表示,因此2个比特16位共可以表示2^16(65536) 个字符,如果大家有印象的话,在介绍Unicode的时候,介绍从0x000000 – 0x10FFFF 一共定义了17个字符平面, 其中第0个平面 作为基本平面 (BMP) ,其 码点表示为 (0x0000 – 0xFFFF) 正好为2个字节,因此对于UCS2编码,其只能表示基本平面的符号,对于其余的符号比如中文,就无法使用UCS2进行编码。
UCS 4由 ISO提出,但是并不常用。作为一种定长编码方式,其固定使用4个比特 映射到UCS字符集。 大家肯定可以想到,相比于变长编码,此种编码方式对于经常使用的英文字符等会额外增加几倍的存储空间,尤其是当早期存储器价格较贵的时候,因此也被其 他编码方式取代了。
UCS / Unicode
可能大家会有一个额外的疑问,在本文的介绍中提出了2个不同的字符集标准,UCS和 Unicode,UCS的全称为 Universal Coded Character Set, 由其名称可知,UCS和Unicode一样,是一套包括了几乎所有字符的通用字符集。实际上,方为了便起见,可以将 两种东西看成一样。它们最初只是2种不同的标准化的产物,Unicode由Unicode 标准委员会提出,此更多算是一种工业标准,额外考虑了更多商业方面的利益,而UCS作为在ISO/IEC 10646基础上的字符集标准,由Unicode委员会和ISO 10646工作组在 1991年合作提出的一套标准,更多是学术方面的规范。 两种标准的大部分码点相同。
7. 参考资料:
-
- Unicode Consortium 官网 http://www.unicode.org/
- Wiki百科 关于UTF 16, UTF 8, Unicode, UCS的介绍
- 阮一峰: 《字符编码笔记: ASCII, Unicode和UTF-8》
- Joel Spolksy: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
- 知乎问题: ‘Windows 记事本的ANSI, Unicode, UTF-8这三种编码模式由什么区别’