关于编码问题

中午突然想起来一直很模糊的有关字符编码问题,而且之前的一个技术面试也被问到了UTF-8编码,当时觉得没有回答好,是应该弄透彻一下。查了一些资料,发现很多人都遇到过这样的问题,然后又很多人的学习笔记可以参考,感觉还是不错的,然后还看了一下RFC3629(UTF-8,a transformation format of ISO/IEC 10646),觉得也应该做一下笔记什么的。可以供日后备忘,也可供大家一起交流学习。

1、计算机编码

由于计算机存储系统的硬件特性---易于表示两种状态,分别可以用0和1表示。因此,计算机内部的数据存储方式是二进制方式存储。每一个位(bit)用0或1表示,位表示数据时太长,因此,用8个位合为一个字节(byte)处理和计数(如表示存储容量的单位GB,TB,PB,ZB等),字节是计算机内部处理数据的基本单位。计算机可以处理的二进制数据又称为机器码,为了将人们可以识别的字符表示出来,必须规定一种编码方式在计算机内部存储,这种编码就称是计算机编码。

2、ASCII编码

ASCII编码是20实际60年代计算机兴起时,以英语字符为背景设计的一套编码系统,ASCII第一次以规范标准的型态发表是在1967年,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(这是以现今操作系统为依归,但在DOS模式下可显示出一些诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符,包含用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。下图为ASCII编码字符表:0000 0000 - 0111 1111(00 - 7F),第一位一律为0。

     ASCII的局限在于只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如naïve、café、élite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则),无法用西欧语言,更加无法用于汉字、日文、韩语等亚洲国家文字。

3、Latin-1编码

  Latin-1是ISO-8859-1的别名,是为了解决西欧等语言而出现的,此编码是单字节编码,将ASCII编码的最高位使用了,一共256个字符,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号 ISO-8859-1收录的字符 除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号 出现的比较晚,没有被收录在ISO-8859-1当中。
 因为ISO-8859-1编码范围使用了单字节 内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流 都不会被抛弃。换言之,把其他任何编码的字节流 当作ISO-8859-1编码看待都没有问题。这是个很重要的特性,如MySQL 数据库默认编码是Latin1就是利用了这个特性。ASCII编码是一个7位的容器,ISO-8859-1编码是一个8位的容器。

4、Unicode 与 UCS (ISO/IEC 10646)

世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。要想打开一个文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。随着互联网的出现,急需出现一种统一的编码方式用来交流,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。历史上有两个组织从事这个工作:

  • 通用字符集(Universal Character Set,UCS): 国际标准化组织(ISO)于1984年创立了ISO/IEC 10646标准定义的标准字符集
       是一种多8bit 的字节字符集,包含了世界上大多数可书写的字符系统。已定义了两种多8bit 的字节编码,对于每一个字符使用4个8bit字节编码的称为UCS-4 每一个字符采用2个8bit字节编码的称为USC-2

          通用字符集包括了其他所有字符集。它保证了与其他字符集的双向兼容,即,如果你将任何文本字符串翻译到UCS格式,然后再翻译回原编码,你不会丢失任何信息。

          UCS包含了已知语言的所有字符。除了拉丁语、希腊语、斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语、格鲁吉亚语,还包括中文、日文、韩文这样的方块文字,UCS还包括大量的图形、印刷、数学、科学符号。ISO/IEC 10646定义了一个31位的字符集。

          ISO/IEC 10646-1标准第一次发表于1993年,现在的公开版本是ISO/IEC 10646-1:2000。ISO/IEC 10646-2在2001年发表。UCS不仅给每个字符分配一个代码,而且赋予了一个正式的名字。表示一个UCS或Unicode值的十六进制数通常在前面加上“U+”,例如“U+0041”代表字符“A”。

  • Unicode:由Xerox、Apple等软件公司于1988年组成的统一码联盟Unicode协会制定的不同于USC的另一套通用字符集
统一码联盟公布的Unicode标准包含了ISO/IEC 10646-1实现级别3的基本多文种平面。在两个标准里,所有的字符都在相同的位置并且有相同的名字。

上述两个组织于1991年前后彼此意识到了统一的重要性,于是合并彼此的工作,然后为创立一个统一的编码表而共同工作,也就是Unicode字符集,从1991年发布Unicode1.0,从Unicode2.0开始采用了ISO 10646-1相同的字库和字码,目前两个项目都存在,各自独立的公布各自的标准。Unicode协会的最新标准是2005年的Unicode4.1.0,ISO的最新标准是10646-3:2003.

5、UTF编码

需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

比如,汉字"严"的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。

这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。

它们造成的结果是:1)出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode。2)Unicode在很长一段时间内无法推广,直到互联网的出现。

互联网的普及强烈要求统一的编码方式,UTF-8是互联网上使用最广的一种Unicode实现方式。还有UTF-16和UTF-32其他实现,不常用。

Unicode:是一种变长的编码方式,可以使用1-4个字节变化表示一个字符,字节多少依据实际表示的字符而变化。规则如下:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

Unicode符号范围 | UTF-8编码方式

(十六进制) | (二进制)
----------------------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

6、多字节编码的字节顺序

Unicode码可以采用UCS-2格式直接存储。以汉字"严"为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。

因此,第一个字节在前,就是"大头方式"(Big endian),第二个字节在前就是"小头方式"(Little endian)。

Unicode规范中推荐使用标记字节顺序的方法是BOM,BOM是Byte Order Mark的简称,在UCS编码中有一个叫做“ZERO WIDTH NO-BREAK SPACE”的字符,其编码是FEFF,而FFFE在USC中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议在传输字节流前,先传输字符“ZERO WIDTH NO-BREAK SPACE”。这样如果接收者收到FEFF,就表示这个字节流是Big-Endian的,如果收到FFFE,就表明这个字节流是Little-Endian的,因此字符“ZERO WIDTH NO-BREAK SPACE”又称为BOM。

UTF-8是以字节为编码单位的,没有字节顺序的问题。不需要BOM来表明字节顺序,但是可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。


参考资料:

1、The absolute Minimum Every Software Developer absolutely,positively Must Know about Unicode and Character  http://www.joelonsoftware.com/articles/Unicode.html

2、Latin-1 百度百科:http://baike.baidu.com/view/1488384.htm

3、ISO/IEC 10646:http://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E5%AD%97%E7%AC%A6%E9%9B%86

4、RFC 2279:UTF-8,a transformation format of ISO 10464: http://www.cse.ohio-state.edu/cgi-bin/rfc/rfc2279.html

5、字符编码笔记:ASCII、Unicode和UTF-8:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

6、程序猿趣味读物:谈谈Unicode编码:http://pcedu.pconline.com.cn/empolder/gj/other/0505/616631_all.html#content_page_1



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值