代码改变世界

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

2009-10-14 14:07  Ivony...  阅读(5213)  评论(7编辑  收藏  举报
其实本来是不想把这部分内容用随笔的方式写出来的(本来打算贴在寒飞雨的这篇文章的回复里),后来阴差阳错机器出了问题,我的长篇大论就此泡汤。只好潦草的在后面回了一个“字符集(charset)和字符编码(encoding)不是一个概念”。因为感觉即使这两个概念混淆,其实问题也不大,所以未做详述。

不过今天看到寒飞雨发来的一个短消息,询问在什么地方能找到字符集和字符编码的详细资料。我尝试去Google了一下,发现以前让我弄明白这件事情的文章很难找到。再加上其实那篇文章主要针对的是Unicode,所以萌生了自己再把这部分内容重写一次的想法。另外寒飞雨的那本书从样张来看的确是很不错,作者本人这种治学的态度也让人很感动。


字符集和字符编码是很容易被搞混的概念,这不奇怪,因为这两个东西并不需要特别严格的区分。

字符集的定义其实就是字符的集合,而字符编码则是指怎么将这些字符变成字节用于保存、读取和传输。

所以可以看出来,应该说抛开字符编码讨论字符集是没有意义的事情。因为字符集并不定义这些字符怎么保存,所以在一开始,这两个概念也没有分开的必要。

ASCII恐怕是全球知名度最高的字符编码方案,ASCII所能表示的所有字符,我们就可以叫做ASCII字符集。值得注意的是ASCII字符集只有128个字符,这是因为ASCII编码是将字符编码成7位二进制的(最高位不用)。

现在熟知的256个字符的版本其实叫做EASCII编码方案,你可能会以为EASCII是一种编码方案,其实不然,他是一大堆基于ASCII编码方案的扩展,详细资料可以参见IEC_8859

当然,这些都是在中文(东方文字)编码出现之前的事情。

西方国家的字符与英文所使用的字符(拉丁字母)大同小异,特殊的诸如什么希腊字母、希伯来字母,个数有限,都可以用某种EASCII编码方案来解决。保存的问题就解决了。但具体在显示的时候,怎么知道储存的字节到底是以哪种编码方案保存的呢?早期的时候,这个东西叫做CodePage。在早期的操作系统(如DOS)就有设置代码页的指令。时至今日,代码页不过是换了一个名目(编码)在各个文字处理软件(Word、IE等)中继续出现。

后来,电脑终于进入了中国,东方文字的复杂多变使得老外的那种EASCII方案也无法解决了。

尽管EASCII方案已经没有办法表示数量庞大的中文字符,但中文字符编码方案却没有丢弃ASCII这个根本。或者说中文字符编码方案是与ASCII兼容的(事实上时至今日与ASCII不兼容的编码都还非常少)。ASCII只定义了7位二进制,一个字节可以表达的字符里,尽管还有128种方案没有被ASCII编码收录,但汉字却不可能只有128种(GB2312字符集收录汉字就有6763个)。为了解决这个问题人们发明了EUC编码方案(同样的,你也不要以为EUC是一种编码方案)。

关于这种编码方案的实现原理我想并不需要我做详细的阐述,其实大家都很明白,就是如果一个字节最高位是0,就被认为是一个ASCII字符,如果最高位是1,则与后面的字节联系起来表示一个字符。

请注意在这个时候,字符集和字符编码这两个概念终于分开来了,或者说字符集第一次跳到了台前,ASCII编码所能表示的字符的集合是没有一个正式的名字的(或者我没找到)。而GB2312其实是中文字符集的名字,而不是中文字符编码的名字(GB2312一般采用EUC-CN编码方案,这是一种基于上述规则用于表示GB2312字符集字符的编码方案)。由于GB2312只定义了字符集而没有定义编码方案,所以很难去指示这个字符集中的一个汉字。为此这个标准为每一个字符约定了一个序号(内码),这就是所谓的区位码。

好像问题都解决了?由于无论是EASCII还是EUC方案,都不能用一种统一的编码方案来表示所有的字符,而且,GB2312其实也没收录所有的中文字符。加之代码页(编码方案)的切换和设置问题很头疼(俗称的乱码),人们希望能够有一种统一的编码方案来表示所有已知语言的字符。

要构筑这个大工程第一步就是找到所有已知语言的字符然后编成一个字符集,这就是Unicode字符集(这个字符集并不是固定大小的,它还在通过发布新版本变大)。Unicode为这个集合的每一个字符都进行了编号,这就是所谓的Unicode编码(或者叫Unicode内码更合适?)。有了字符集,还要有字符编码才能用于保存和读取,Unicode字符的编码方案一般被称为Unicode Translation Format,即UTF,这一次你猜对了,UTF也不是一种编码方案。最常见的是UTF-8。

请不要以为编纂字符集是一件很容易的事情,其实这里面问题太多了。因为我们有时候很难分清字符和字型的区别,尤其是对于东方语言而言。例如吁和籲到底是算一个字符还是两个字符?这只是简繁的差别。还有跨语言的差别,例如日文中的中文字和中文中的中文字算一个字符?还是两个字符?韩文、越南文呢?大篆、金文里面的古中文字符,与现代的演变后的字符又算不算一个?这些乱七八糟的问题就留给科学家们去搞定吧,我们只需要用现成的Unicode标准就好了。


那么字符集和字符编码在日常使用中有什么区别呢?
通过上面的资料,我们就能知道,字体(库)是与字符集有关系的,而所谓的乱码,则是与字符编码有关系的。现在好像还没有什么字体能涵盖unicode字符集。部分中文字体只能涵盖GB2312字符集。所以所谓的“宋体-方正超大字符集”大家应该明白是什么意思了。


延伸阅读:

“Unicode编码”到底指什么?
“Unicode编码”又是一个容易搞混的概念。大体上,Unicode编码可以指:
1、字符在Unicode字符集内的序列编码,这是一个32位的二进制数(不过包括规划的字符,24位都足够了)。以前这是一个16位的二进制数(古老的事情)。
2、UTF-16编码方案,这是一种使用两到四个字节表示Unicode字符的编码方案(因为Unicode字符现在连24位二进制的表都摆不满)。值得注意的是UTF-16编码方案有个版本,一个叫大尾序,一个叫小尾序,鉴于网上能普遍找到的东西我就懒得写了,所以就此略过。UnicodeEncoding类就是这种编码方式的实现。

这两者看起来什么关系都没有,怎么会都被叫做Unicode编码呢?因为对于大多数常见的Unicode字符而言,UTF-16编码方案两个字节连起来就是Unicode序号。