kang000feng +

VIM 中文编码问题

Posted by Alfred Sun Vim

VIM 在 Linux 中的配置文件是 .vimrc,而在 Windows 中是 _vimrc。通过设置这个文件可以定制自己的 VIM 编辑器。

VIM 运行环境

VIM 运行环境设定了5个地方,其路径都列在 runtimepath 选项中,使用 set 命令查看:

:set runtimepath?

显示结果如下:

runtimepath=~/.vim,/usr/local/share/vim/vimfiles,/usr/local/share/vim/vim63,/usr/local/share/vim/vimfiles/after,~/.vim/after

以上路径都是使用“ VIM 语言”给出的,一般情况下,$VIMRUNTIME 不会被定义为系统环境变量,$VIM 在少数情况下会在 VIM 之外被定义。如果你想知道这些路径都是什么,可以在 VIM 中使用 :echo $VIMRUNTIME:echo $VIM 查看。

以上提到的 5 个目录的子目录结构都是相同的。如果你希望在其它目录里安装插件的话,建议使用 $VIMRUNTIME 的目录结构作为模版,将必要的目录结构创建完整,像这样:

+ <Directory>
  +  colors
  +  compiler
  +  doc
  +  etc
  +  ftdetect
  +  ftplugin
  +  indent
  +  keymap
  +  plugin
  +  syntax

中文编码乱码

VIM 7.0 之后对双字节编码的支持已经很不错了,通过一些配置可以识别双字节编码。

修改配置文件

set encoding=utf-8
set fenc=cp936
set fileencodings=cp936,ucs-bom,utf-8
"判断操作系统
if(has("win32") || has("win95") || has("win64") || has("win16"))
    let g:iswindows=1
else
    let g:iswindows=0
endif
"防止菜单乱码
if(g:iswindows==1)
    source $VIMRUNTIME/delmenu.vim
    source $VIMRUNTIME/menu.vim
    set langmenu=zh_CN.utf-8
    language messages zh_CN.utf-8
endif
"默认以双字节处理那些特殊字符
if v:lang =~? '^\(zh\)\|\(ja\)\|\(ko\)'
    set ambiwidth=double
endif
set nobomb

解释说明:

set encoding=utf-8 将 Vim 的内部编码格式变为 utf-8 ,这样识别文件正确的准确性会提高很多。
set fenc=cp936 是指当新建一个文件的时候,默认编码是 gbk。
set fileencodings=cp936,ucs-bom,utf-8 会让 Vim 按照 gbk、utf-8(没有头)、utf-8 的顺序识别。如果读者需要默认为 utf-8 格式,可以改变这个变量取值的顺序。
set nobomb 让 Vim 不自动设置字节序标记,因为并不是所有编辑器都可以识别字节序标记。

fencview.vim 插件

Vim 内置的编码识别机制,识别率是很低的,尤其是对于简体中文(GBK/GB18030)、繁体中文(Big5)、日文(euc-jp) 和韩文(euc-kr)之间的识别。FencView 这个插件对其功能进行了完善。

这款插件集成了自动检测编码格式的能力,使用词频统计的方式识别编码,正确率非常高。
不过,建议关掉自动检测,只有在 Vim 识别失败的时候,才调出 FencView,手动选择编码。本文开头给出了 Vim 环境的知识,从中选择合适的位置安装这个插件即可。

在 vimrc 中配置如下:

"关闭自动检测
let g:fencview_autodetect=0
map <F11> :FencView<cr>

这样按下 F11 键就可以直接呼出 FencView 界面,再按下就会关闭。

另外,按照上面的格式来配置 Vim,在保存文件时,是不会更改文件格式的,如果想要强制更改,例如要改成 utf-8,可以用 set fenc=utf-8 来执行,之后写入即可。

编码设置选项

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

1、encoding

encoding 是 Vim 内部使用的字符编码方式。当我们设置了 encoding 后,Vim 内部所有的 buffer、寄存器、脚本中的字符串等,全都使用这个编码。Vim 在工作的时候,如果编码方式与它的内部编码不一致,它会先把编码转换成内部编码。如果工作用的编码中含有无法转换为内部编码的字符,这些字符就会丢失。因此,在选择 Vim 的内部编码的时候,一定要使用一种表现能力足够强的编码,以免影响正常工作。可以用命令 :set enc 查看当前的 enc 是什么值。

由于 encoding 选项涉及到 Vim 中所有字符的内部表示,因此只能在 Vim 启动的时候设置一次。在 Vim 工作过程中修改 encoding 会造成非常多的问题。如果没有特别的理由,请始终将 encoding 设置为 utf-8。为了避免在非 UTF-8 的系统如 Windows 下,菜单和系统提示出现乱码,可同时做这几项设置:

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

2、fileencoding

Vim 解析出来的当前文件的编码(有可能解析成错的哦)。

当 Vim 从磁盘上读取文件的时候,会对文件的编码进行探测。如果文件的编码方式和 Vim 的内部编码方式不同,Vim 就会对编码进行转换。转换完毕后,Vim 会将 fileencoding 选项设置为文件的编码。当 Vim 存盘的时候,如果 encodingfileencoding 不一样,Vim 就会进行编码转换。因此,通过打开文件后设置 fileencoding,我们可以将文件由一种编码转换为另一种编码。
但是,由前面的介绍可以看出,fileencoding 是在打开文件的时候,由 Vim 进行探测后自动设置的。因此,如果出现乱码,我们无法通过在打开文件后重新设置 fileencoding 来纠正乱码,设置 fenc 只能改变文本的编码格式。

3、fileencodings

Vim 解析文件时猜测的编码格式顺序列表。

编码的自动识别是通过设置 fileencodings 实现的,注意是复数形式。fileencodings 是一个用逗号分隔的列表,列表中的每一项是一种编码的名称。当我们打开文件的时候,Vim 按顺序使用 fencs 中的编码进行尝试解码,如果匹配成功,就用该编码方式进行解码,并将 fileencoding 设置为这个值,如果解码失败,就继续试验下一个编码。

因此,我们在设置 fileencodings 的时候,一定要把要求严格的、当文件不是这个编码的时候更容易出现解码失败的编码方式放在前面,把宽松的编码方式放在后面。

例如,latin1 是一种非常宽松的编码方式,任何一种编码方式得到的文本,用 latin1 进行解码,都不会发生解码失败——当然,解码得到的结果自然也就是理所当然的“乱码”。因此,如果你把 latin1 放到了 fileencodings 的第一位的话,打开任何中文文件都是乱码也就是理所当然的了。

以下是博主推荐的一个 fileencodings 设置:

set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1

其中,ucs-bom 是一种非常严格的编码,非该编码的文件几乎没有可能被误判为 ucs-bom,因此放在第一位。

utf-8 也相当严格,除了很短的文件外(例如许多人津津乐道的 GBK 编码的“联通”被误判为 UTF-8 编码的经典错误),现实生活中一般文件是几乎不可能被误判的,因此放在第二位。

接下来是 cp936gb18030,这两种编码相对宽松,如果放前面的话,会出现大量误判,所以就让它们靠后一些。cp936 的编码空间比 gb18030 小,所以把 cp936 放在 gb18030 前面。

至于 big5euc-jpeuc-kr,它们的严格程度和 cp936 差不多,把它们放在后面,在编辑这些编码的文件的时候必然出现大量误判,但这是 Vim 内置编码探测机制没有办法解决的事。由于中国用户很少有机会编辑这些编码的文件,因此我们还是决定把 cp936gb18030 往前提以保证这些编码的识别。

最后就是 latin1 了。它是一种极其宽松的编码,以至于我们不得不把它放在最后一位。不过可惜的是,当你碰到一个真的 latin1 编码的文件时,绝大部分情况下,它没有机会 fall-backlatin1,往往在前面的编码中就被误判了。不过,正如前面所说的,中国用户没有太多机会接触这样的文件。

如果编码被误判了,解码后的结果就无法被人类识别,于是我们就说,这个文件乱码了。此时,如果你知道这个文件的正确编码的话,可以在打开文件的时候使用 ++enc=encoding 的方式来打开文件,如:

:e ++enc=utf-8 myfile.txt

4、termencoding

终端使用文本编码,或者说是 Vim 用于屏幕显示的编码,在显示的时候,Vim 会把内部编码转换为屏幕编码,再用于输出。内部编码中含有无法转换为屏幕编码的字符时,该字符会变成问号,但不会影响对它的编辑操作。如果 termencoding 没有设置,则直接使用 encoding 不进行转换。

举个例子,当你在 Windows 下通过 telnet 登录 Linux 工作站时,由于 Windows 的 telnet 是 GBK 编码的,而 Linux 下使用 UTF-8 编码,你在 telnet 下的 Vim 中就会乱码。此时有两种消除乱码的方式:一是把 Vim 的 encoding 改为 gbk,另一种方法是保持 encodingutf-8,把 termencoding 改为 gbk,让 Vim 在显示的时候转码。显然,使用前一种方法时,如果遇到编辑的文件中含有 GBK 无法表示的字符时,这些字符就会丢失。但如果使用后一种方法,虽然由于终端所限,这些字符无法显示,但在编辑过程中这些字符是不会丢失的。

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

[Comments]:

Related Blog