跟我一起学习Vim--Vim乱码

在Vim中,有四个与编码有关的选项,它们是:fileencodings、fileencoding、encoding 和termencoding。在实际使用中,任何一个选项出现错误,都会导致出现乱码。因此,每一个Vim用户都应该明确这四个选项的含义。下面,我们详细介绍一下这四个选项的含义和作用。

1. encoding(enc)

encoding简写为enc,是Vim内部使用的字符编码方式。通常情况下,enc和locale相同,即enc的默认值是由你的locale来决定的。当我们设置了encoding之后,Vim内部所有的buffer、寄存器、脚本中的字符串等,全都使用这个编码。Vim在工作的时候,如果编码方式与它的内部编码不一致,它会先把编码转换成内部编码。如果工作用的编码中含有无法转换为内部编码的字符,在这些字符就会丢失。因此,在选择 Vim 的内部编码的时候,一定要使用一种表现能力足够强的编码,以免影响正常工作。

由于encoding选项涉及到Vim中所有字符的内部表示,因此只能在Vim启动的时候设置一次。一般将 enc设置为UTF-8。一般做设置如下:

1
2
3
set encoding=utf-8
set langmenu=zh_CN.UTF-8
language message zh_CN.UTF-8

2. fileencoding(fenc)

fileencoding简写为fenc,当Vim从磁盘上读取文件的时候,会对文件的编码进行探测。如果文件的编码方式和Vim的内部编码方式不同,Vim读取文件时就会自动将文件由fenc转换为enc,在写入文件时,又将 enc转换为fenc。当fenc设置为空时,fenc会使用enc的值(所以在读写文件时不会发生编码转换)。当读取一个文件时,Vim会自动确定文件的编码,它依次尝试fileencodings(fencs)列表中的每一种编码,如果尝试成功,就将成功的那个值设置为fenc的值,如果尝试不成功,将fenc设置为空,即fenc的值和enc相同。

心得:fenc是用来标识文件的文本编码。

3. fileencodings(fencs)

fileencodings,简称为fencs。编码的自动识别是通过设置fencs实现的,注意是复数形式。fencs是一个用逗号分隔的列表,列表中的每一项是一种编码的名称。当我们打开文件的时候,VIM按顺序使用 fileencodings中的编码进行尝试解码,如果成功的话,就使用该编码方式进行解码,并将fenc设置为这个值,如果失败的话,就继续试验下一个编码。因此,我们在设置fencs的时候,一定要把要求严格的、当文件不是这个编码的时候更容易出现解码失败的编码方式放在前面,把宽松的编码方式放在后面。大体设置如下: set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1 其中,ucs-bom是一种非常严格的编码,非该编码的文件几乎没有可能被误判为ucs-bom,因此放在第一位。utf-8也相当严格,除了很短的文件外,现实生活中一般文件是几乎不可能被误判的,因此放在第二位。接下来是cp936和gb18030,这两种编码相对宽松,如果放前面的话,会出现大量误判,所以就让它们靠后一些。cp936的编码空间比gb18030小,所以把 cp936放在gb18030前面。至于big5、euc-jp和euc-kr,它们的严格程度和cp936差不多,把它们放在后面,在编辑这些编码的文件的时候必然出现大量误判,但这是Vim内置编码探测机制没有办法解决的事。由于中国用户很少有机会编辑这些编码的文件,因此我们还是决定把cp936和gb18030前提以保证这些编码的识别。最后就是latin1了。它是一种极其宽松的编码,以至于我们不得不把它放在最后一位。如果编码被误判了,解码后的结果就无法被人类识别,于是我们就说,这个文件乱码了。

4. termencoding(tenc)

termencoding,简称为tenc。是Vim用于屏幕显示的编码,在显示的时候,Vim会把内部编码转换为屏幕编码,再用于输出。内部编码中含有无法转换为屏幕编码的字符时,该字符会变成问号,但不会影响对它的编辑操作。如果tenc没有设置,则直接使用enc,不进行转换。举个例子,当你在Windows下通过telnet登录Linux工作站时,由于Windows的telnet是GBK编码的,而Linux下使用UTF-8编码,你在telnet下的Vim中就会乱码。此时有两种消除乱码的方式:一是把Vim的 enc改为gbk,另一种方法是保持enc为utf-8,把tenc改为gbk,让Vim在显示的时候转码。显然,使用前一种方法时,如果遇到编辑的文件中含有GBK无法表示的字符时,这些字符就会丢失。但如果使用后一种方法,虽然由于终端所限,这些字符无法显示,但在编辑过程中这些字符是不会丢失的。

对于图形界面下的GVim,它的显示不依赖TERM,因此tenc对于它没有意义。在GTK2下的GVim 中,tenc永远是utf-8,并且不能修改。而Windows下的GVim则忽略tenc的存在。

5. 乱码的原因和解决方案

也许你在阅读了上面一章节关于4个选项的描述后,虽然对概念有所理解,但可能还是有迷惑,不知道该如何正确设置这些选项的值,也可能不知道该如何解决乱码之类的问题。下面将说明乱码的原因和解决方案。要介绍为何会乱码,我们先来看这3个关键的因素:

1
2
3
(1)locale 
(2)enc
(3)tenc

首先解释一下locale,如果你是Linux/Unix环境,那么对 locale应该比较熟悉,它主要是用于设置你系统的语言环境的,其实Window下也是一样的,Windows也有自己的locale设置(通常都是默认为zh_CN.GBK(或者GB2312/GB18030)。无论是哪个系统,你只要记住locale是用于设置系统级别的语言环境的就可以了。在Linux/Unix系统下,locale的值就是你Shell所设置的值,这时,最简单的情况是:enc的值设置为locale的值,tenc的值设置为enc的值,这3个值相同,所以是不会乱码的。在Windows系统下,情况则有所不同,因为你通常是用终端软件(例如putty/Xshell)从Windows登陆到Linux系统来使用VIM的,这个时候你可以认为locale的值是终端软件设置的值,当然locale本身的意义不是这样的,只不过这个时候 Shell本身设置的locale对于Windows下的文本输入和显示是无关紧要的,所以你可以将Windows下的这个终端环境当做你VIM 运行的环境,也就可以认为终端软件设置的locale和Linux下设置locale是同类效果的。

tenc设置是用于转换文本输入和显示的,如果你选择Default Language,那么XShell将使用Windows系统的locale值,通常都是zh_CN.GB2312,也就是说你输入的文本是以GB2312来编码的,显示的时候也是用GB2312来解释文本的。这里,我们就可以简单的认为,VIM的locale环境的编码的值为GB2312。 解释完locale,我们来解释enc,enc是用于VIM做内部表示的,也就是说VIM内部处理的是以enc编码的文本流,所以实际上enc的值和显示是否乱码没有根本上的必要联系。只不过enc的值通常是tenc的取值来源而已,所以这里暂且将它列举为可能引起乱码的一个因素。最重要的其实是tenc的值,tenc是用于告诉 VIM终端的locale值是什么的,那么,如果tenc告诉 VIM的值是错误的话,那就肯定乱码了,所以其实乱码的最根本原因就是tenc的值和locale的值不同而造成的,具体来说:

(1)在Linux/Unix环境下,locale的值与tenc 的值不同 
(2)在Windows环境下,终端软件设置的 Encoding(可以认为是locale)的值与tenc的值不同 

所以解决乱码的问题自然就是使得tenc的值和locale的值相同就可以了。在Linux/Unix环境下,最简单的就是将enc的值设置和locale相同就可以了,Windows下则主要是要将终端的Encoding值设置为与VIM的tenc值相同就可以了。假如你要编辑的文件是UTF8格式的,那么其实不管你的终端的Encdoing的值如何设置,只要你将VIM的tenc值设置为与终端Encoding的值相同就可以正确显示了,其实这就是告诉VIM正确的识别终端的文本编码方式,就这么简单,大家统一口径,问题就解决了。

请注意,上面的这个简单例子是假设你的文件的内容本身是正确的,如果内容本身不正确,那无论如何都不能显示正确,因为内容本身就是乱码。这个说法可能比较怪异,举个例子会相对明白些。tenc和你的终端的Encoding的值是一样的,否则你无论如何输入,都将显示乱码,原因是如果你的tenc和终端的Encoding不同,那么VIM对你输入的文本的编码解释将是错误的,例如如果你的Encoding是GBK,这意味着你输入的GBK码,但是tenc设置的却是utf8 ,这意味着你告诉VIM你的输入是uft8码,这样VIM会把你的GBK码按utf8的编码方式来解释,最后在写入文件时,VIM将会以utf8转换为enc码(不管enc是什么编码)的方式来写入,但实际上应该是按GBK码转enc码的规则来写入文件,这很明显是错误的,所以文件本身的内容就是乱码了,你再怎么搞都是乱码。

坚持原创技术分享,您的支持将鼓励我的继续创作