代码改变世界

关于字符集和字符编码以及代码页的前前后后(续)

2009-10-15 12:59  Ivony...  阅读(2890)  评论(6编辑  收藏  举报
老实说其实这个话题很大,所以上一篇文章很多东西都一笔带过了,主要是拣网上难找到的来说。但写完后发现还是有很多问题没有说清楚。所以就有了这篇续。

关于字符集和字符编码,虽然是两个概念,但它们之间存在着对应的关系。在早期(ASCII年代),这个关系是一一对应的,所以很容易把这两者混为一谈。简单的说一个字符集可以有多种编码实现(例如Unicode字符集),同样的,一个字符编码也可以表示多个字符集的并集,例如EUC-CN编码就能表示GB2312和ASCII字符集的并集。

用离散数学的观点来说。字符编码是以某个字符集为定义域,以字节序列集合为值域的单射函数。

很显然,在这两个集合之间,可以存在无数多个单射函数。


关于UTF-16的大尾序和小尾序。其实很多人会奇怪为什么一个编码方案还要分什么大尾序小尾序。因为UTF-16只是定义了如何将Unicode字符映射为一个或多个16位二进制数,但却没有定义16位二进制数怎么用字节来表示。如果用两个字节来表示这个16位二进制数,将这个数的低八位放在前面的字节,就叫做小尾序,即UTF-16LE(这是Windows平台通常的处理方式)。

但如果不是用字节,而是用一个16位的整数来表示UTF-16编码,就不存在大尾序和小尾序了。.NET Framework中的System.Char结构就是这样一个东西,Char是一个16位整数,使用UTF-16编码来表示Unicode字符。由于UTF-16编码并不总能用一个16位二进制数来表示一个Unicode字符,所以Char也不总是能表示Unicode字符。或者说一个Char是不能表示所有的Unicode字符的

大尾序和小尾序并不是字符编码的概念,其实这是一个定义多字节数据(例如16位的整数)如何用字节保存的规范。所以用32位二进制表示一个Unicode字符的UTF-32编码也存在大尾序和小尾序。而用一个或多个8位二进制表示一个Unicode字符的UTF-8方案就没有什么大尾序小尾序。


讨论完UTF-16,我们继续回到字符编码和字符集。

刚才我们有说到每一种字符编码都有所对应的能表示的字符集。所以在字符编码之间转换时,还要考虑其所能表示的字符集的范围。

如果源编码所能表示的字符集不是目标编码所能表示的字符集的子集,就可能导致转换不成功。
由于几乎所有的字符集都是Unicode字符集的子集,所以几乎任何编码格式都可以安全的转换为UTF-xx编码(所以用sting或char来储存从别的编码转换来的字符是安全的)。但UTF-xx编码却不总能成功的转换为其他的编码。

同样的,由于所谓的GB2312编码方案其实就是EUC-CN编码方案,其所能表示的字符集是GB2312和ASCII的并集,所以ASCII字符编码总能安全的转换为EUC-CN编码,反过来则不行。


上一篇文章说到,字体是与字符集有关系的。其实大部分的中文字体都只包含了GB2312字符集的字符(宋体除外)。所以在使用某些中文字体的时候,我们能发现很多诸如“籲”这样的繁体字是无法显示的。还有更过分的中文字体,连ASCII字符集都不包括,这个时候系统就会用默认的英文字体来显示ASCII字符(或者其他规则)。

但并不是所有的字符集所定义的字符都是显示字符,例如ASCII就包含很多无法显示的控制字符,字体里面自然也不会包含这些字符。很多英文字体字体所能显示的字符很多时候都只是ASCII的一个子集。或者是多个EASCII方案字符集的并集的子集。