#TITLE: VIM 文件編碼識別與亂碼處理
edyfox
在 Vim 中,
有四個與編碼有關的選項,
它們是:
''fileencodings''、
''fileencoding''、
''encoding'' 和 ''termencoding''。
在實際使用中,
任何一個選項出現錯誤,
都會導致出現亂碼。
因此,
每一個 Vim 用戶都應該明確這四個選項的含義。
下面,
我們詳細介紹一下這四個選項的含義和作用。
* encoding
''encoding'' 是 Vim 內部使用的字符編碼方式。
當我們設置了 ''encoding'' 之后,
Vim 內部所有的 buffer、
寄存器、
腳本中的字符串等,
全都使用這個編碼。
Vim 在工作的時候,
如果編碼方式與它的內部編碼不一致,
它會先把編碼轉換成內部編碼。
如果工作用的編碼中含有無法轉換為內部編碼的字符,
在這些字符就會丟失。
因此,在選擇 Vim 的內部編碼的時候,
一定要使用一種表現能力足夠強的編碼,
以免影響正常工作。
由于 ''encoding'' 選項涉及到 Vim 中所有字符的內部表示,
因此只能在 Vim 啟動的時候設置一次。
在 Vim 工作過程中修改 ''encoding'' 會造成非常多的問題。
如果沒有特別的理由,
請始終將 ''encoding'' 設置為 ''utf-8''。
為了避免在非 UTF-8 的系統如 Windows 下,
菜單和系統提示出現亂碼,
可同時做這幾項設置:
#Code syntax="
vim"
<
<
---
set encoding=utf-8
set langmenu=zh_CN.UTF-8
language message zh_CN.UTF-8
* termencoding
''termencoding'' 是 Vim 用于屏幕顯示的編碼,
在顯示的時候,
Vim 會把內部編碼轉換為屏幕編碼,
再用于輸出。
內部編碼中含有無法轉換為屏幕編碼的字符時,
該字符會變成問號,
但不會影響對它的編輯操作。
如果 ''termencoding'' 沒有設置,
則直接使用 ''encoding'' 不進行轉換。
舉個例子,
當你在 Windows 下通過 telnet 登錄 Linux 工作站時,
由于 Windows 的 telnet 是 GBK 編碼的,
而 Linux 下使用 UTF-8 編碼,
你在 telnet 下的 Vim 中就會亂碼。
此時有兩種消除亂碼的方式:
一是把 Vim 的 ''encoding'' 改為 ''gbk'',
另一種方法是保持 ''encoding'' 為 ''utf-8'',
把 ''termencoding'' 改為 ''gbk'',
讓 Vim 在顯示的時候轉碼。
顯然,
使用前一種方法時,
如果遇到編輯的文件中含有 GBK 無法表示的字符時,
這些字符就會丟失。
但如果使用后一種方法,
雖然由于終端所限,
這些字符無法顯示,
但在編輯過程中這些字符是不會丟失的。
對于圖形界面下的 GVim,
它的顯示不依賴 TERM,
因此 ''termencoding'' 對于它沒有意義。
在 GTK2 下的 GVim 中,
''termencoding'' 永遠是 ''utf-8'',
并且不能修改。
而 Windows 下的 GVim 則忽略 ''termencoding'' 的存在。
* fileencoding
當 Vim 從磁盤上讀取文件的時候,
會對文件的編碼進行探測。
如果文件的編碼方式和 Vim 的內部編碼方式不同,
Vim 就會對編碼進行轉換。
轉換完畢后,
Vim 會將 ''fileencoding'' 選項設置為文件的編碼。
當 Vim 存盤的時候,
如果 ''encoding'' 和 ''fileencoding'' 不一樣,
Vim 就會進行編碼轉換。
因此,
通過打開文件后設置 ''fileencoding'',
我們可以將文件由一種編碼轉換為另一種編碼。
但是,
由前面的介紹可以看出,
''fileencoding'' 是在打開文件的時候,
由 Vim 進行探測后自動設置的。
因此,
如果出現亂碼,
我們無法通過在打開文件后重新設置 ''fileencoding'' 來糾正亂碼。
* fileencodings
編碼的自動識別是通過設置 fileencodings 實現的,
注意是復數形式。
fileencodings 是一個用逗號分隔的列表,
列表中的每一項是一種編碼的名稱。
當我們打開文件的時候,
VIM 按順序使用 fileencodings 中的編碼進行嘗試解碼,
如果成功的話,
就使用該編碼方式進行解碼,
并將 ''fileencoding'' 設置為這個值,
如果失敗的話,
就繼續試驗下一個編碼。
因此,
我們在設置 ''fileencodings'' 的時候,
一定要把要求嚴格的、
當文件不是這個編碼的時候更容易出現解碼失敗的編碼方式放在前面,
把寬松的編碼方式放在后面。
例如,
latin1 是一種非常寬松的編碼方式,
任何一種編碼方式得到的文本,
用 latin1 進行解碼,
都不會發生解碼失敗
--
當然,
解碼得到的結果自然也就是理所當然的“亂碼”。
因此,
如果你把 ''latin1'' 放到了 ''fileencodings'' 的第一位的話,
打開任何中文文件都是亂碼也就是理所當然的了。
以下是滇狐推薦的一個 ''fileencodings'' 設置:
#Code syntax="
vim"
<
<
---
set
fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1
---
其中,
ucs-bom 是一種非常嚴格的編碼,
非該編碼的文件幾乎沒有可能被誤判為 ucs-bom,
因此放在第一位。
utf-8 也相當嚴格,
除了很短的文件外
(例如許多人津津樂道的 GBK 編碼的“聯通”被誤判為 UTF-8 編碼的經典錯誤),
現實生活中一般文件是幾乎不可能被誤判的,
因此放在第二位。
接下來是 cp936 和 gb18030,
這兩種編碼相對寬松,
如果放前面的話,
會出現大量誤判,
所以就讓它們靠后一些。
cp936 的編碼空間比 gb18030 小,
所以把 cp936 放在 gb18030 前面。
至于 big5、euc-jp 和 euc-kr,
它們的嚴格程度和 cp936 差不多,
把它們放在后面,
在編輯這些編碼的文件的時候必然出現大量誤判,
但這是 Vim 內置編碼探測機制沒有辦法解決的事。
由于中國用戶很少有機會編輯這些編碼的文件,
因此我們還是決定把 cp936 和 gb18030 前提以保證這些編碼的識別。
最后就是 latin1 了。
它是一種極其寬松的編碼,
以至于我們不得不把它放在最后一位。
不過可惜的是,
當你碰到一個真的 latin1 編碼的文件時,
絕大部分情況下,
它沒有機會 fall-back 到 latin1,
往往在前面的編碼中就被誤判了。
不過,
正如前面所說的,
中國用戶沒有太多機會接觸這樣的文件。
如果編碼被誤判了,
解碼后的結果就無法被人類識別,
于是我們就說,
這個文件亂碼了。
此時,
如果你知道這個文件的正確編碼的話,
可以把 ''fileencodings'' 改成只有這一種編碼,
阻止任何 fall-back 發生,
然后重新打開這個文件。
* fencview
根據前面的介紹,
我們知道,
通過 Vim 內置的編碼識別機制,
識別率是很低的,
尤其是對于簡體中文 (GBK/GB18030)、
繁體中文 (Big5)、
日文 (euc-jp)
和韓文 (euc-kr) 之間的識別。
而對于普通用戶而言,
肉眼看出一個文件的編碼方式也是很不現實的事情。
因此,
滇狐強烈推薦水木社區的 mbbill 開發的 fencview 插件。
該插件使用詞頻統計的方式識別編碼,
正確率非常高。