(原创)关于中文编码的深入分析……
把今天遇到的问题跟大家分享,也算是给自己留下一点技术资料……
不过这只是一些个人的实验结论和推理,如果有说错的地方,还请高手纠正……
话说,今天无比蛋疼的想telnet一下水木清华bbs,结果连上去发现竟然是乱码,没办法,改LANG呗,改成了zh_CN.gbk依然是乱码,结果突然就蛋疼了,为啥用GBK解码还是不行呢?
后来经过多方查证,原来改LANG是不管用的,只能设置gnome-terminal的菜单,把utf8改成gbk,这下就正常了,但此时LANG已经改回了en_US.utf8,这让我很费解,为啥LANG还是utf8,却能显示gbk了呢?把LANG再改成zh_CN.gbk,显示依然正常,也就是说跟LANG没关系,为什么会这样呢?最后得出的结论是:LANG只有在纯命令行下才对字符的显示生效(话说没有第三方程序,纯文字界面根本显示不了中文),而当打开了xwindows以后,显示的工作就由gnome接手了,terminal是gnome的一部分,所以自然也是不受LANG支配的,这就好像一个软件究竟用什么方式来显示汉字是这个软件的自由,不必非得服从操作系统。到此,telnet乱码的问题解决了,那么LANG又是干嘛的呢?等下再说,再回过头来看看telnet乱码的问题。
通过这个问题,我们很容易举一反三,用gnome-terminal的时候,ssh乱码的问题怎么办?ftp的乱码问题怎么办?都是一样的办法,改LANG没有用,只能修改gnome-terminal的菜单配置(或者用其他方法,但终究是改gnome)。并且,这一理论一样可以延伸到一些非网络的本地乱码问题,为啥vi的乱码要配置.vimrc才行?(其实调整gnome-terminal也行,不过麻烦一些,后面会说到)为啥gedit乱码要设置gconf-editor才行?LANG都是不管用的。不难看出,在linux中使用图形界面存在着一个问题,就是解码层面(系统默认码)和显示层面(字符显示)存在着一点点的不协调,无法进行统一部署,对于显示层面的乱码问题,只能通过修正gnome才能解决……(还有解码层面的乱码问题,后面再说)
那LANG就没用了吗?必须不是,LANG代表的是linux的系统默认码(实际上这么说不全面,因为这涉及到locale的配置,以及LANG, LC_*, LC_ALL这些变量之间关系的问题,这里就简单的把它理解为系统默认码就好了),什么叫系统默认码,其实就是系统默认的汉字编码方式。
这里还需要特别特别强调的一点,就是windows的系统默认码不是GBK,而是utf16,也就是windows对待中文是使用utf16作为默认的编码格式,但是为什么很多人都一提到windows就会想到gbk呢?原因就是windows几乎所有的中文文本存储都是采用GBK的格式,而且绝大多数中文软件都特别喜欢使用GBK这种编码格式来存储和显示软件内部和外部的中文信息,所以才造成大家都误以为windows的系统默认编码是GBK。(现在的windows都是全球统一版本,当然是用utf16做系统默认码,如果用GBK岂不是专门为中国定做?unicode表示的符号比gbk多多了)事实上,如果有人mount过一个windows硬盘就会发现,linux默认的utf8编码对windows硬盘下中文的文件名和目录名几乎毫无压力,而在linux若以GBK方式建立一个中文文件名的文件,在windows中是看不到的,这就表示windows对于文件名,目录名都是采用unicode编码存储的,并不是GBK。只不过几乎从没有人考虑过文件名和目录名的编码方式,而大多都关注文件数据和软件的编码方式,所以很多人都不留意这个事情,实际上windows的系统默认码无法更改,留意也没用……(一些软件,比如ftp会在解析和上传文件的时候把文件名和目录名的编码方式也改掉,导致连文件名和目录名也变成了GBK,但这是软件本身的问题,并不是windows的问题)
知道了系统默认码以后,我们就可以继续分析更深一步的乱码问题了,也就是解码层的乱码,而不再是显示层的乱码。如果做这么一个实验,大家会发现一个非常有趣的现象,这个实验就是在windows下写一个GBK的中文文本,然后在linux下用vi打开,这个时候,
只有LANG=zh_CN.gbk和terminal配置为gbk显示,才是正确的,这不稀奇。
有趣的是,
LANG=zh_CN.gbk和terminal配置为utf8显示,
LANG=en_US.uft8和terminal配置为gbk显示,
LANG=en_US.uft8和terminal配置为utf8显示,
这三种情况会出现三种完全不同的乱码……
为什么?因为这里涉及到的不仅仅是显示的问题了,还有存储的问题,要知道,不同的编码方式,磁盘存储也是不同的……
所以,在第一种情况下,linux认为文本是GBK编码(这是对的),所以按照GBK的编码格式来读取磁盘文件,读取到内存以后,LANG的任务就完成了,剩下的就是显示,terminal把这些01代码按照utf8的规则来进行解释,显示的结果必然是错的……(实际上,telnet的乱码就是这种形式的乱码)
第二种情况下,linux认为文本是utf8编码(这就错了),所以就按照utf8的规则来读取磁盘文件,读出来的东西自然什么都不是,虽然terminal按照GBK规则进行显示,但是已经不可能显示正确了……
第三种就不说了,从读文件到显示,错的都没边了……
这也就是为什么会出现3种不同的乱码的原因了,说句题外话,windows就不会出现这种问题,原因在于windows并不会按照系统默认码来解析文件(那岂不是全都乱码了),而是会根据不同文件的文件头来判断文件类型(windows并不是只通过扩展名来判断文件类型的),所以无论什么编码方式都能自动识别,不像linux什么都得手动识别……
再来回顾一下刚才说的,linux的编码问题的确让人蛋疼,因为必须同时满足解码正确和显示正确两部过程才能最终得到正确结果,一步错则乱码之。除了文本以外,这也就是为什么linux和windows之间的压缩文件总是出问题的原因了,其实这个和文本显示文字的道理是完全一样的,linux下的压缩会默认将文件中所有中文(包括文件名和目录名)按照utf8规则进行重新编码,写成一个文件,而解压缩则会默认按照utf8规则进行解码,生成许多文件,windows反之以GBK来做这些事情,所以这两个系统的压缩包几乎无法通用,全都是乱码……
以上就是今天遇到的一些问题,以及我得出的一些结论,希望对大家有帮助,如果有错,还请高手拍砖……
------解决方案--------------------支持,总结是个好习惯
------解决方案--------------------MARK!!
------解决方案--------------------现在的windows都是全球统一版本,当然是用utf16做系统默认码,如果用GBK岂不是专门为中国定做?unicode表示的符号比gbk多多了
这个我表示不是非常赞同。全球统一版本?
------解决方案--------------------另外说一下,一个文件是gbk编码,终端使用gbk编码,用vim打开那个文件,set encoding=gb