中午突然想起来一直很模糊的有关字符编码问题,而且之前的一个技术面试也被问到了UTF-8编码,当时觉得没有回答好,是应该弄透彻一下。查了一些资料,发现很多人都遇到过这样的问题,然后又很多人的学习笔记可以参考,感觉还是不错的,然后还看了一下RFC3629(UTF-8,a transformation format of ISO/IEC 10646),觉得也应该做一下笔记什么的。可以供日后备忘,也可供大家一起交流学习。
1、计算机编码
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编码
4、Unicode 与 UCS (ISO/IEC 10646)
世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。要想打开一个文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。随着互联网的出现,急需出现一种统一的编码方式用来交流,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。历史上有两个组织从事这个工作:- 通用字符集(Universal Character Set,UCS): 国际标准化组织(ISO)于1984年创立了ISO/IEC 10646标准定义的标准字符集
通用字符集包括了其他所有字符集。它保证了与其他字符集的双向兼容,即,如果你将任何文本字符串翻译到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的另一套通用字符集
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