編碼自動識別工具 uchardet
This post is written in Chinese. Please consider using Google Translate
最近在給OpenCC做圖形界面,遇到一個問題:OpenCC默認只能轉換utf-8文本,其他編碼像GB18030,BIG5只能轉換成utf-8以後,纔能用OpenCC轉換。這個問題說大不大,說小也不小。我完全可以增加一個選項,在打開的時候讓用戶選擇文本編碼,然後再轉換就行了,但這卻給用戶非常糟糕的體驗,因為很多非專業用戶根本不知道什麼是文本編碼,更別說辨別了。GB18030/BIG5硬要用utf-8打開的話,肯定會遇到亂碼。由於Windows默認是GB18030/BIG5編碼,一般情況下文本會被保存成默認編碼,這樣更大大增加了用戶遇到亂碼的概率。爲了提高體驗,我計劃實現文本編碼的自動檢測。
最早接觸到編碼是從做網站開始的,記得如果忘了在head中顯式地向瀏覽器指定編碼,就經常會出現亂碼,但亂碼也並不總是出現,這是怎麼回事呢?瀏覽器還是有自動識別的能力的。發現Firefox瀏覽器中有一個編碼選項,裏面有「自動檢測」,使用它絕大多數時候都能正確識別。
事實上純文本的編碼檢測是一個非常複雜的問題,甚至理論上根本不可能實現。確切地說,「檢測」應該叫「探測」或者「推測」纔更恰當。自動編碼探測的實現原理主要是統計學的方法,每個編碼會有一定的特徵,首先檢測特徵是否符合,再使用常用的匹配,類似於蒙特卡羅法。具體方法可以參考Mozilla。
mozilla在很多年前就做了一個非常優秀的編碼檢測工具,叫chardet,後來有發佈了算法更加優秀的universalchardet,用於Firefox的自動編碼識別。我想,這麼出名的一個工具,應該肯定已經有不少人在用了。有意思的是,我在網上找到了chardet和universalchardet的各種移植:
- python-chardet Python 移植
- ruby-rchardet Ruby 移植
- juniversalchardet Java 移植 universalchardet
- jchardet Java 移植 chardet
- nuniversalchardet C# 移植 universalchardet
- nchardet C# 移植 chardet
惟一沒有的,竟然是C/C++的接口封裝。debian更是收錄了python-chardet和ruby-rchardet,卻沒有libchardet或者libuniversalchardet。莫非沒有C/C++的應用在使用chardet嗎?用強大的Google代碼搜索,發現的確有,但幾乎都是把chardet的代碼內嵌到了項目中,耦合十分緊密。更有直接調用python-chardet的,實現不夠純淨。
總覺不該是這樣,但經過反復確認,真的沒有一個獨立的universalchardet的C函數庫封裝。還是自己動手好了,我從mozilla上面取下來了代碼,做了一點點補丁,寫了一個接口和命令行界面,取名uchardet,大功告成。測試了一些GB18030和UTF8的文本,感覺準確率非常高,而且速度很快。但是當我試圖識別幾個字節的短文本的時候,卻出現了識別錯誤,開始以為是我的錯,後來發現我用Firefox直接打開,也是無法識別的,而且錯誤識別的編碼一樣。看來是上游的問題,應該是算法本身的缺陷吧。想想看,畢竟文本越短歧義的可能性越強。不過既然能達到和Firefox同樣的水平,一般應用也就够了。
項目主頁在Google code上:
http://code.google.com/p/uchardet/
代碼在github上:
https://github.com/BYVoid/uchardet
我爲什麽用universalchardet?其實編碼自動識別的解決方案不止一個,有icu提供的解決方案,IE也有API,還有已經在很多Linux發行版中的enca。我之所以用universalchardet,是因為它是最合適的。IE的API不能跨平臺,icu實現太龐大,enca是GPL(注意不是LGPL),使用它意味著我也要讓我的所有源碼使用GPL,而不是更加開放的Apache。universalchardet是MPL的,和LGPL差不多寬鬆,使用它是沒有問題的。我非常不喜歡以GPL發佈的函數庫,這給開發者的限制太大了。