在Ubuntu中有个文件,名为fname,通过file fname命令,显示为UTF-8 Unicode text。
在windows下通过xshell远程访问Ubuntu,通过命令cat fname,显示正常。而通过命令vim fname,则乱码。文件中包含中文、数字、字母。
下图是cat命令的显示结果。
下图是vim fname的显示结果。
首先排除xshell的xterm设置问题,因为已经设置为utf-8,而且在cat下都能正常显示中文。
file命令已经表明这个文件是utf-8编码的文本文件。我们猜测,很可能vim打开文件时,使用了其他的解码方式,来理解这个文件。或者vim内部表示的编码方式无法兼容中文(CJK)。从而在vim内部,就乱了,输出到显示设备的时候,自然无法正确显示。
为了定位问题,我做如下对比验证。
通过Notepad++建立两个文件,f1_ch_utf_8和f1_ch_gbk分别是utf-8和gbk编码,内容都是“中文”。在Ubuntu环境下通过vim打开,都显示的乱码,但是乱的形式不同。如下图:
还是同样的编码方式,但是把内容改为纯字母“zhongwen”,保存的文件名分别为f1_alpha_utf_8和f1_alpha_gbk,看看它们的显示结果。
如下,都是“正确”的显示结果。
这就有点意思了。我们可以断定,vim一直在用同一个解码方式来解读它输入的文件。但神奇的是,当内容为纯字母“zhongwen”时,无论保存文件时的编码方式是utf-8还是gbk,在当前的vim设置下都能正确的显示结果。但是,如果包含“中文”,就无法正确显示了,且乱码的形式都不一样。
如果你理解ASCII码、GBK、UTF-8和Unicode之间的关系,以及它们的实现方式,上面看似神奇的事,就一点也不神秘了。
这不是一两篇博客能解释清楚的。
回到我们当前面临的问题,如何解决。就是怎么设置vim使得它能够面对不同的编码方式的文件时,能自动调用正确的解码方式来解读该文本文件,并且保证在vim内部(内存的buffer中,寄存器中)都能正确的表示中文抽象字符。在输出时,再以设定的编码方式保存到磁盘或者传输到网路中。
vim的环境变量encoding决定了如何在内部表示抽象字符。如果想正确编辑处理包含CJK文字的文件,该编码格式必须与中文相对应的,否则即便能正确的从文件中解码,也无法在内存中表示。其实也不可能正确解码。
例如:我encoding设置为Latin-1(也就是ASCII码),它无论如何也无法表示中文这样的至少占两个字节的汉字编码,必然会出现“乱码”。所谓乱码,就是编码、解码、内部存储方式无法匹配。
现在在home目录下打开(或者新建)home目录下的~/.vimrc文件,输入set encoding=utf-8,然后打开之前的文件,发现f1_cn_utf-8能正确显示,但是f1_cn_gbk还不能。看来问题还没真正解决。
继续研究发现vim还有一个环境变量就是fileencodings,它可以有好几个值,用逗号隔开。vim会依次尝试用它设定的编码方式来解码文件,直到没有发生错误,并把该编码方式赋值给fileencoding。注意,没有写错,fileencoding与fileencodings是两个不同的环境变量。
到此,~/.vimrc的内容如下:
set encoding=utf-8
set fileencodings=ucs-bom,utf-8,cp936
当打开不同编码的文件是可以通过命令“:set fileencoding”显示的值可能是cp936或utf-8
现在看下打开f1_cn_utf-8和f1_cn_gbk的结果:
双双显示成功!
正确设置后,用vim打开文章最开始的文件结果如下:
环境变量fileencoding=utf-8