GB18030,想说爱你不容易
之所以会有此感叹,并非觉得GB18030的编码框架有什么不妥,而是对该标准在实际应用中遇到的尴尬情形的一些个人看法。
疑惑一:GB18030到底是一种ANSI编码还是Unicode编码?
这本来是毫无疑问的,作为对GB2312和GBK的扩展,GB18030势必要向下兼容这两个编码标准,而GB2312和GBK都是不折不扣的ANSI编码,那么建立在这两个标准基础之上的GB18020也理所当然的应该是一种多字节的ANSI编码了。
既然是ANSI编码,那么在实际的系统中,它就应该是作为一种本地化的编码字符集出现的。比如在Windows里面,它就应该像GBK、Big5这些ANSI编码那样,有自己的代码页和相应的代码页属性(主要指引导字节和尾随字节的取值范围)。
对于代码页,微软已经给出了答案:Win2k以上的操作系统只要安装了微软的GB18030-2000扩展支持包,就可以支持GB18020-2000编码标准的54936代码页了。我在安装了18030支持包之后,根据微软给出的测试建议,写了个调用API函数IsValidCodePage(54936)的小程序,测试结果是54936代码页可用(未安装18030支持包以前,测试54936代码页不可用)。
但仅仅是某个代码页可用并不表示文本处理程序就可以处理基于该代码页编码的文本了,还必须把这个代码页设为系统的当前代码页才行。可是,18030扩展包不是自带了一个GB18030?Unicode的文本编码转换程序的嘛,这个程序并不需要把系统的当前代码页设为54936就可以工作呀。
其实,关于这一点花点时间想一下也很简单:文本文件的内码转换其实根本不需要操作系统的支持,这种转码工作在计算机文本处理任务中来说算是最简单的了――说得极端一点,只要有转码表,我们甚至可以写一个在DOS下运行的GB18030?Unicode转码程序,如果省去中文界面的话,这个程序连中文系统都不用加载。在现实应用中,文本处理工作远不只转码这一项,就拿日常用得比较多的文本编辑来说吧――假设有一款以ANSI方式来处理文本的字处理软件,如果想要在这个软件中正确显示和编辑GB18030编码的文本的话,首先就应该把系统的当前代码页设为54936才行。这是因为代表GBK的936代码页的属性和代表GB18030的54936代码页的属性有很大的不同,在936代码页下,ANSI程序是无法正确处理GB18030编码的文本的。
那么把系统的当前代码页设为54936不就好了吗?可是事情好像并没有那么简单……
在编程时,通过调用API函数GetACP可以获得系统当前的代码页,可是Win32 API里面并没有对应提供一个类似”SetACP”这样的设置当前代码页的函数。事实上即使是Windows自己,也没有办法直接改变系统当前的代码页, 改变当前代码页的方法是在控制面板中设置区域和语言选项,然后重启才能生效。Windows区域和语言选项里面的”非Unicode程序语言版本”设置对 于普通电脑用户来说就是为那些只能处理特定语言的程序(ANSI文本处理程序)指定一个正确的,默认可以处理的语言种类(比如是中文、日文或韩文);而对 于开发ANSI文本处理程序的程序员来说,其实就是设置系统当前的代码页。比如要设置950代码页的时候,选”中文(台湾)”,要设置936代码页的时候,就选”中文(中国)”。
可是让人难以理解的是,在安装了18030支持包以后,”代码页转换表”里面已经显示”54936(GB18030 简体中文)”代码页是可用使用的了。但在”非Unicode程序语言版本”列表里面,却没有相应地提供一个类似”中文(中国GB18030)”这样的选项,也就是说:Windows根本就没有提供给我们把代表GB18030编码标准的54936代码页设为系统当前代码页的选择。
难道微软并不认为GB18030是一种ANSI编码,不需要相应的代码页支持?可是这个结论显然跟微软提供了54936这个代码页是相矛盾的,因为代码页本身就是处理不同本地化语言的一种机制,生来就是为ANSI编码而设的。而且18030支持包里面的ReadMe文档也透露了它附带的转码程序其实是调用了API函数MultiByteToWideChar和WideCharToMultiByte来实现的(否则无法解释这个程序怎么会只有区区25KB之小),这就是说微软确实是把GB18030当作一种多字节的ANSI编码来看待的。可是现在这个编码的代码页却根本不能设置为系统的当前代码页!这恐怕在Windows所支持的所有代码页里面,是唯一的一个吧。
疑惑二:54936代码页的真相?
既然CP54936在所有的代码页中显得如此之特殊,这不得不让人产生了一窥其具体技术细节的欲望。我们知道:对于代码页机制来说,不同代码页间的主要区别在于各个位置上的字节取值范围的不同。比如CP936的引导字节范围在0x81-0xFE之间,尾随字节范围在0x40-0x7E或0x80-0xFE之间;CP932的引导字节范围在0x81-0x9F或0xE0-0xFC之间,尾随字节范围在0x40-0xFC(0x7F除外)之间。显然CP936和CP932有 很大的不同,这种不同成了系统在当前代码页下,区分哪些是有效编码(字符)和无效编码(字符)的依据。
言归正传,虽然Windows需要重启才能改变当前 代码页,但要查看某个代码页的信息却不用那么麻烦。在任意一个代码页下,随时都可以用API函数GetCPInfo读出包括当前代码页在内的所有系统支持 的代码页的属性信息。
我们先用最熟悉的CP936试看看,测试结果GetCPInfo返回的信息如下:MaxCharSize=2;LeadByte={81,FE,00,00,00,00,00,00,00,00,00,00}。这个结果的意思是说:CP936的字符最大编码长度是两个字节,它的引导字节有一个取值范围:0x81-0xFE。显然GetCPInfo(936)返回的信息跟我们所了解的GBK编码标准的技术框架是符合的。
接下来看看我们所关心的CP54936的情况,测试结果GetCPInfo返回的信息如下:MaxCharSize=4;LeadByte={00,00,00,00,00,00,00,00,00,00,00,00}。按照Windows代码页机制的说法,这个结果意味着CP54936没有引导字节取值范围的定义,而这种情况通常只有单字节ANSI编码才会出现。
难怪Windows没有提供把54936设为系统当前代码页的选项了,因为即使把CP54936设为当前代码页,那些ANSI字处理程序(也就是非Unicode程序)也是无法显示和编辑GB18030编码的文本的。
这一点可以很容易地从Windows的代码页技术原理上分析得到:CP54936没有定义引导字节的取值范围,但它本身又是一种ANSI编码的代码页,所以当系统运行在这个代码页下的时候,实际上等于是运行在单字节ANSI编码的代码页下面。
这样一来,当我们试图在ANSI程序里面显示一个四字节编码的GB18030字符的时候,系统会毫不客气地把它识别为无效编码(字符),实际显示出来的就是一堆的问号了。这是因为单字节ANSI编码的每个字节取值都不会超过0x7F的,而GB18030的四字节编码的第一、三个字节的取值都在0x81以上,根据Windows代码页的技术规则,当然会被判定为无效编码(字符)了。
注:Windows的代码页技术原理并不难理解。系统里面对应不同的代码页存储了不同的代码页属性――包括这个代码页所代表的编码标准的最大字符存储长 度(单位:字节);引导字节取值范围;尾随字节取值范围(这个是隐藏的,用GetCPInfo函数是得不到它的,但它确实存在,看来微软还是习惯性地留了 一手)。
系统存储这些代码页属性最主要的目的就是用来判断某个给定的字符串里面是否包含有不符合当前代码页编码框架的字符(编码),当然这种判定是对于那 些准备要送去给程序中需要显示在用户界面上的字符串(文本)而言的。对于发生在计算机内部(内存)里面的文本处理过程(比如文本文件的内码转换),是不需 要进行这种判断的。
这也就是为什么说文本文件的转码不需要特定系统的支持的原因。系统判断有效编码(字符)和无效编码(字符)的过程是这样的:顺序读取字 符串中每个字节值,如果出现一个取值不在当前代码页属性许可的取值范围(比如在单字节代码页中出现大于0x7F的字节)内的字节的话,那么这个字节所属的字符就是无效字符,在软件界面上就会被显示为问号。
对于双字节编码的代码页(Windows支持的代码页都是单字节或双字节代码页,只有CP54936是四字节这个例外),如果读出的某个字节值小于等于0x7F,就说明它是一个单字节字符;如果读出的字节值大于0x7F, 就把它当成引导字节处理,看看这个字节值是否在当前代码页指定的引导字节取值范围内,以及它的下一个字节值是否在当前代码页指定的尾随字节取值范围内。如 果这两个条件都成立,说明当前读到的这个字节和它的下一个字节组成了一个有效的双字节字符,否则当前字节值判为无效编码,显示为问号。
上述这个判定过程是真实存在的,完整的编码有效性判定工作需要引导字节取值范围和尾随字节取值范围两个基本信息的支持。只是不知道微软出于什么原因,只 开放了引导字节取值范围,但从Windows在文本处理上的一些行为中,是可以发现尾随字节取值范围信息是肯定存在的。
比如,在简体中文系统下(CP936)写一个ANSI的小程序:这个程序的界面包括一个Edit控件和一个Button控件(两个控件的字体属性已经设置成了”宋体-18030″),编写Button控件的鼠标单击过程实现把一个编码字节值序列为的ANSI字符串赋值给那个Edit控件让它显示”䢎”(0x82348434是CJK ExtA汉字”䢎”的GB18030编码)这个字的功能。做这个实验程序的目的是要测试在CP936代码页下,ANSI程序是否能支持GB18030编码字符的显示和编辑。该程序的运行结果是,按下Button按钮之后,Edit编辑框里面并没有如希望的那样显示”䢎”,而是??。
显然Windows认为在当前代码页(CP936)下面,0x82348434是两个非法字符。因为这个编码字节序列由四个字节组成,当系统读出第一个字节0x82的时候,因为这个字节值大于0x7F,所以需要判断它和下一个字节0x34能否组成一个双字节字符,而由于0x34不在GBK尾随字节取值范围之内,所以第一和第二字节被判定为无效编码;而第三字节的0x84和第四字节的0x34也是一样的。所以这四个字节组成的编码序列被判定为两个无效字符,在软件界面(Edit控件)上被显示成了两个问号。
系统的这一处理结果反映了尾随字节取值范围信息的真实存在,否则很难想象它怎么会把0x8234判断为无效字符。因为根据双字节ANSI编码框架的一般规则:组成双字节字符的前后两个字节中,第一个字节必须大于0x7F以便文本处理程序把这个字节和单字节字符编码区分开来。而对第二个字节的取值范围并不作特别的规定,可以是任意取值范围(包括小于等于0x7F的ASCII编码值范围)。如果系统中没有存储GBK尾随字节取值范围定义的话,由于第一个字节0x82已经符合GBK引导字节取值范围的要求了,第二个字节不管取什么值都应该判定这两个字节组成的字符是有效的双字节字符。而事实是Windows准确地判断出了0x8234不符合GBK的双字节编码框架,这只能说明它是清楚地知道GBK的尾随字节取值范围定义的。
实验分析到这里,结论已经很明显了:在目前正式发行的Windows操作系统,包括微软声称能支持GB18030的Win2k、WinXP和Win2003中,即使安装了GB18030-2000扩展支持包,也不能真正使这些系统支持GB18030编 码标准。
因为道理再简单不过了,程序员根本就没有办法编写出能在这些系统下显示和编辑GB18030-2000四字节编码文本的ANSI文本处理程序。不 是很清楚这些系统是怎么通过GB18030-2000的标准符合性测试的。因为这些产品并不完全符合其中的”体系正确性:产品必须能够正确识别和处理按照 国家标准GB 18030进行编码的文本文件。”这项要求。
而微软提供的GB18030支持包充其量只是提供了一个字体和几个修改过的字符编码转换API函数,以及一个无法使用的CP54936,最后用一个其实跟系统支不支持GB18030标准并没有关系的内码转换小程序就蒙混过关了。
为什么GB18030制定出来已经6年了,却一直没有得到有效的应用?为什么我们大部分的应用程序还只能停留在处理GBK字符集的阶段?为什么当我们很多公民在电脑中录入自己包含生僻字的姓名 时仍然只能用×来代替?
其实这并不怪GB18030-2000的编码技术框架不好,而是相关部门在推广应用该标准上虎头蛇尾的态度造成的。单以编码框架而 言,虽然一直被很多人视为Unicode/ISO10646的中国版(实际上也是)。但技术上还是有它自己独到之处的,即使它的四字节编码框架跟传统的单/双字节ANSI编码相比显得不是那么的标准,但还不至于另类到无法处理的地步。
个人认为它的四字节编码格式跟UTF-16编码格式的代理对机制在处理思想上很有些相似之处,从应用层来说,单独某款软件要识别和处理这种单、双、四字节混合编码并不困难。
但要把这些技术融入到整个操作系统之中,并不是简单地修改几个API就能完成的,单是把Win32 API里面所有有关Ansi字符串处理的函数列出来就够吓人一跳的了。因为微软不可能单为了一个GB18030放弃其他的ANSI编码标准,加入对GB18030的 支持必须是确保兼容原有的代码页机制的前提下才能实施的。而要保证这一点,就必须对几乎所有的字符串处理API进行一定的修改。
总之以当年的情形,微软是 很难在短时间之内拿出完整的解决方案的。政府压得急,微软草草地拿出了个换汤不换药的支持包应付了事,没想到还真通过了。也不知道政府是不是真的不清楚这 个支持包的底细,反正自从这场”GB18030-2000标准之争”以微软推出扩展支持包这个皆大欢喜的体面方式收场之后,双方好像就没把推广应用这回事 放心上了。
几年中就一直任由应用层面上这种不尴不尬的局面持续下来,把一班满心期待迎接GB18030时代却又不明就里的中国应用开发人员撩在了一边。这些人在经过了几次徒劳的尝试之后,终于还是放弃了支持GB18030标准的念头,要么退回到GBK的小窝,要么踏上了Unicode未知的前程。总之在大家看来结论都是一样的:对于Windows,GB18030是不可用的。
最近听说新版的GB18030-2005已经于去年年底制定出来了,虽然尚未有幸一睹标准原文真容,但希望它不会步GB18030-2000的后尘。落 个不尴不尬的局面吧,因然从长远来说主流肯定是Unicode,但短期内仍然还是ANSI和Unicode并存的局面。像GB18030这种本地化版本的Unicode还是需要的。希望它的应用价值不要被再度埋没了。虽然这不是我这种小人物的努力就可以决定的。
via http://blog.csdn.net/jaminwm/article/details/8033805
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/42862.html