Vim 打开中文文件乱码:encoding、fileencodings 与 fileencoding
从 Windows 同事那里拿到一个配置文件,用 Vim 打开,中文全变成了
ÖÐÎIJâÊÔ。网上搜到一行配置加进 vimrc,这个文件好了,另一个 UTF-8 的文件却开始显示成浣犲ソ。
Vim 处理文件编码时涉及三个选项,名字非常相似,作用完全不同:
encoding:Vim 内部使用的编码,也是界面显示用的编码。现代系统上应该是utf-8。fileencodings(复数):打开文件时依次尝试的编码列表,第一个能成功转换的就被采用。fileencoding(单数):这个文件被识别成的编码,也是保存时使用的编码。
乱码几乎都出在第二个:探测列表的内容和顺序。本文在 Vim 9.1 上用 GBK、UTF-8、带 BOM 的 UTF-8 文件验证几种配置的结果。实验见文末。
一、默认配置把 GBK 识别成了 latin1
utf8.txt 的字节:e4 b8 ad e6 96 87 e6 b5 8b e8 af 95 0a
gbk.txt 的字节:d6 d0 ce c4 b2 e2 ca d4 0a
默认 fileencodings = ucs-bom,utf-8,default,latin1
打开 gbk.txt → fileencoding=latin1,第一行:ÖÐÎIJâÊÔ
打开 utf8.txt → fileencoding=utf-8,第一行:中文测试Vim 按顺序尝试:没有 BOM,ucs-bom 不成立;GBK 的字节不是合法的 UTF-8,utf-8 失败;default 是当前语言环境的编码,在 UTF-8 环境下也失败;最后的 latin1 能接受任何字节序列,于是它总会「成功」。每个 GBK 字节被当成一个西欧字符显示出来,就是 ÖÐÎÄ。
latin1 放在最后是有意的:它保证任何文件都能打开,并且保存时不改变原有的字节。
按 latin1 打开 GBK 文件、加一个字符后保存:d6 d0 ce c4 b2 e2 ca d4 21 0a(原有字节未变)所以看到乱码时不要急着保存修改,乱码只是显示的问题,原样保存不会损坏文件;但如果在乱码状态下修改了中文内容,写进去的就真的是错误的字节了。
二、把 gbk 加进列表:顺序决定一切
fileencodings = ucs-bom,utf-8,gbk,latin1
打开 gbk.txt → fileencoding=cp936,第一行:中文测试
打开 utf8.txt → fileencoding=utf-8,第一行:中文测试在 utf-8 之后、latin1 之前加上 gbk,两种文件都能正确识别(Vim 内部把 gbk 记为 cp936)。网上常见的配置也是这个顺序。
顺序反过来就会出问题:
fileencodings = ucs-bom,gbk,utf-8,latin1
打开 UTF-8 的「中文测试」 → fileencoding=utf-8,第一行:中文测试
打开 UTF-8 的「你好」 → fileencoding=cp936,第一行:浣犲ソGBK 是双字节编码,很多 UTF-8 字节序列恰好也能被当成合法的 GBK 解读。「你好」的 UTF-8 字节被 GBK 成功「转换」了,于是 Vim 采用了 GBK,显示出 浣犲ソ;「中文测试」碰巧包含 GBK 里不存在的组合,转换失败,才轮到 UTF-8。同样的配置,是否乱码取决于文件内容,这正是开头那个「这个好了、那个坏了」的原因。
规则:严格的编码放在前面,宽松的放在后面。UTF-8 的结构有很强的校验性,随机字节几乎不可能是合法的 UTF-8,所以它应该排在 GBK 之前;latin1 什么都接受,永远放在最后。
三、手动指定与转换保存
探测失败时,不必改配置,用 ++enc 重新打开当前文件:
:e ++enc=gbk gbk.txt → fileencoding=cp936,第一行:中文测试要把文件转换成 UTF-8,以正确的编码打开后修改 fileencoding 再保存:
以 gbk 打开后 set fileencoding=utf-8 再保存:e4 b8 ad e6 96 87 e6 b5 8b e8 af 95 0a带 BOM 的 UTF-8 文件由 ucs-bom 识别,保存时默认保留 BOM;set nobomb 后保存可以去掉它:
带 BOM 的文件 set nobomb 后保存:e4 b8 ad e6 96 87 0aBOM 在 Windows 的一些工具里是识别 UTF-8 的依据,但在 Shell 脚本(第一行的 #! 前多了三个字节)、CSV 的第一个字段名、JSON 解析器里常常造成问题。
四、推荐配置
set encoding=utf-8
set fileencodings=ucs-bom,utf-8,gbk,latin1新建的文件 fileencoding 为空,保存时使用 encoding 的值,也就是 UTF-8、不带 BOM,不需要再在 vimrc 里设置 fileencoding(它是缓冲区选项,在 vimrc 里设置只影响启动时的那个缓冲区)。
encoding 要在 vimrc 的开头设置,修改它会影响已经加载的内容。更根本的做法是让团队的文件统一使用 UTF-8,并在仓库的 .editorconfig 里写明 charset = utf-8。字节、码点和字形的区别,见一个表情是几个字符。
五、检查清单
- 先用
:set fileencoding?看 Vim 把文件识别成了什么。 fileencodings里 UTF-8 在 GBK 之前,latin1在最后。- 识别错误时用
:e ++enc=编码重新打开,不要在乱码状态下修改中文。 - 转换编码:正确打开后
:set fileencoding=utf-8,再保存。 - 不需要 BOM 的文件
:set nobomb。
配套实验
- codesphere-labs/engineering/vim-file-encoding:Vim 9.1:默认与修改后的 fileencodings 对 GBK、UTF-8、带 BOM 文件的探测,++enc,保存时的转换与 nobomb(验证记录)
参考资料
- Vim 帮助:
:help 'encoding'、:help 'fileencodings'、:help 'fileencoding'、:help ++enc、:help 'bomb' - EditorConfig 文档