分类:
2009-12-18 14:18:06
我对ASCII,ISO-8859-1,Unicode,gb2312,gbk,gb18030,utf-8,utf-16这些概念一直很模糊,搞不清楚,但是最近我碰到了一些问题,因此我仔细研究了这些概念决定总结一下自己Unicode相关的知识。希望这篇文章能帮助大家理解这些概念。当然参考了些网上的文章。
我喜欢把总结写在前面,这样如果大家看完我的总结能理解,就不必浪费时间往下看了,呵呵。
总结:
字符集就这么几个,ASCII字符集,ISO-8859-1字符集,gb系列,unicode系列。最开始美国人自己搞了个ASCII字符集,自己用的很high,但是没有考虑到其他语言的国家,后来西方不说英语的发达国家不愿意了,搞了ISO-8859-1字符集,西方世界的一些其它语言,如西班牙语、法语、德语、意大利语都够用了。再随着发展中国以及一些亚洲国家不愿意了,但是亚洲国家的字体非常繁琐,然后中国就起草了gb系列字符集,有汉字日语假名等等,随着一起发展的还有unicode系列字符集,这个系列比较特殊,想用unicode表示世界上所有的字符,但是当时有两个组织在起草,后来经过一些商议决定合并。最后就出来以unicode为主题,衍生出来的utf-8,utf-16,uft-32等。以上属于我个人的理解。
具体如下:
ASCII字符集
7位的编码方案,总共表示128个字符,其中包括了大小写英文字母、数字、标点符号等常用字符。英语足够应付。
ISO-8859-1字符集(兼容了上面的ASCII字符集)
也称ISO-Latin字符集,它扩展了ASCII字符集,用到了8bit字节里的最高一位,这样它就有256个字符,前128个字符和ASCII字符集相
同。有了ISO-Latin字符集,西方世界的一些其它语言,如西班牙语、法语、德语、意大利语都够用了。
GB系列字符集(GB2312,GBK,GB18030)
最开始时GB2312,后来发现字不够用,所有了GBK,然后又发展出了GB18030。都是向前兼容且兼容ASCII字符集的。
一个字节是无论如何也表达不了哪怕是最长用的汉字字符集的,所以为了用计算机存储汉字,必须使用多个字节。多字节字符集就是使
用可变长的编码长度来编码字符,有的字符用一个字节编码,比如ASCII字符,有的字符用两个字节编码,比如汉字。GB系列的字符集和
ISO-Latin字符集一样,前128个字符和ASCII字符集相同。GB系列字符集是兼容的,相同的中文字符在这3个字符集里有相同的编码。GB2312
和GBK一个字符最多2个字节表示,GB18030可多达4个字节。在这种编码里表示汉字时,需要一个leading byte,它总是大于127,这个字节
的含义是说明它和后面的字节(们)一起表示一个字符。
Unicode字符集:
每个地区的人都试图扩展ASCII编码来支持本地的语言,最终的结果是导致互不兼容。因为除了最低的128个字符相同以外,其它的字符
都使用自己特殊的编码方案。
当使用与文件保存时的编码方案不同的编码来读取文件时,就会产生错误——比如Windows记事本那个著名的“联通BUG”。
统一所有字符的编码是Unicode被设计出来的初衷。
长久以来,Unicode在我心中的概念就是:使用2个字节来编码字符,使用Unicode可以表示世界上所有的字符。但这种理解并不准确!
其实Unicode可以看成是一种理想:这种理想就是世界上的所有字符都只有一个唯一的标识!至于怎样去实现这种理想,有很多的实现
方式:UTF-8,UTF-16,UTF-32,甚至在Unicode标准里还介绍了一种压缩的实现方式。Unicode把这个唯一的标识称之为代码点(code point)
,字符的代码点以U+XXXX的方式表示,这个可以打开Windows自带的字符映射表看得到。
Unicode最初被设计出来的时候希望使用2个字节就可以表示世界上的所有字符。因此,实现Unicode最直接的想法就是用两个字节来存
储一个字符,如果大家都这么想就好了,这样一个字符就可以用2个字节长的短整形来存储。但是偏偏还有一个叫做大端小端东西存在,这
样2个字节的短整型在内存中的表示顺序就有2种可能,这就是为什么当用记事本保存文本文件时可以选择Unicode或者Unicode big endian
的原因。
1个字符=2个字节在现实中却遇到了麻烦。一方面,用2个字节表示一个字符,浪费了大量的空间(如果仅仅用来存储ISO-Latin字符集
里的字符的话),而且还会有大端小端的问题,解决的方案是UTF-8编码;另一方面,人们在实践中发现即使用2个字节编码也无法表示所有
字符,因此出现了UTF- 16。UTF-16除了使用2个字节编码外,还使用一对2个字节来表示Unicode里很少用到的字符;另外还有UTF-32,它使
用单独的4个字节来编码所有的Unicode字符。
UTF-8编码
“用2个字节来表示一个英语字母这太浪费”,顾名思义,8说明UTF-8编码中最小的单位是8bit的字节。采用UTF-8编码,Unicode代码
点中U+007F以下(包含U+007F)的字符用一个字节编码,其它的字符用多个字节编码,最多一个字符用4个字节编码。这样UTF-8兼容ASCII
,但是不兼容ISO-Latin字符集。
Unicode字符集采用UTF-8编码方案时的对照表:
Unicode代码点区间 UTF-8编码后的结果
U-00000000 - U-0000007F 0xxxxxxx
U-00000080 - U-000007FF 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
网上的很多文章都有这个表,可以看到一个Unicode代码点采用UTF-8编码时最多可达6个字节。但是从Unicode官方网站上看到的是UTF
-8编码的最大字节长度是4个字节。也就是说最下面的两行没有了。并且第四行的范围是从U-00010000到U-00110000。
UTF-8编码的实现方式比较好理解:例如“汉”字的Unicode编码是6C49,6C49在0800-FFFF之间,所以最终编码应该是3个字节。6C49的
二进制位串是:110110001001001,把这个位串从右向左填充到那3个字节的x部分,高位不够的用0补。最终得到的3个字节是:11100110
10110001 10001001,即E6 B1 89。注意由于UTF-8的最小编码单元是字节,所以不存在大端小端的问题。在各种Unicode编码方案之间转换
的标准算法(诸如从UTF-16到UTF-8或者反过来)已经有了,在Unicode的官方网站上可以找到。
这样Unicode至少就有5种编码方案了(UTF-8,UTF-16两种,UTF-32两种),怎么区分它们呢?
区分各种不同Unicode编码方案的技巧被称为Byte Order Mark(BOM)。将BOM插入到文件的开头,应用程序就能知道接下来应该使用哪
种编码来解析文本了,以ANSI编码保存的文件没有BOM。
Byte order mark Description
EF BB BF UTF-8
FF FE UTF-16, little endian(VC的Unicode用的就是这种格式)
FE FF UTF-16, big endian
FF FE 00 00 UTF-32, little endian
00 00 FE FF 00 00 FE FF
关于Unicode编程:
当然最常见的问题是多字节字符集,与Unicode的各种编码之间怎么转换。Unicode与UTF8之间有一一对应的关系,它们之间可以直接相
互转换,多字节字符集和UTF8之间的转换得经过Unicode中转。
MBCS--Unicode--UTF8
UTF8--Unicode--MBCS
在Windows平台上,进行转换的函数是WideCharToMultiByte和MultiByteToWideChar。这样如果要将MBCS转换成UTF8,先调用
MultiByteToWideChar,使用CP_ACP代码页(默认的代码页),然后调用WideCharToMultiByte,这次用CP_UTF8作代码页。
解释一下记事本的那个"联通"的BUG
当以ANSI编码保存联通两个字时,文件里的内容如下:
c1 aa cd a8
1100 0001 1010 1010 1100 1101 1010 1000
记事本在打开文件的时候会去猜测文本文件的编码方式。由于第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与
UTF8规则里的两字节模板是一致的,记事本猜测文件是以UTF-8的编码方式保存的,所以不能正确的显示。
而如果使用记事本的打开菜单,强制以ANSI编码的方式打开文件则能够正确得显示联通两个字。而如果你在"联通"之后多输入几个字,
其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解
读之,这时乱码又不出现了。
网上看到有人提到这个观点:“记事本为什么没有使用BOM呢?使用ANSI编码存储的文本文件是没有BOM的啊!既然没有BOM,它应该以
ANSI编码读取文件才合理啊!只能解释说记事本在很久以前就在使用“猜”技术来揣度文件的编码了。”