1. 程式人生 > >利用Android原始碼,輕鬆實現漢字轉拼音功能

利用Android原始碼,輕鬆實現漢字轉拼音功能

 今天和大家分享一個從Android系統原始碼提取出來的漢字轉成拼音實現方案,只要一個類,560多行程式碼就可以讓你輕鬆實現漢字轉成拼音的功能,且無需其他任何第三方依賴。

需求場景

實際開發過程中需要用到實現漢字轉成拼音的場景比較常見,如:通訊錄裡的聯絡人字母導航欄,為沒有設定頭像的使用者生成一個名字首字母的頭像,國家(省份、城市)字母導航欄,搜尋關鍵字轉換成拼音等。


實現方案

Android平臺上將漢字轉換成為拼音已經有一些開源的第三方實現方案,如pinyin4j和TinyPinyin

以上這兩個實現方案,都需要引入不少類以及一些相應的編碼檔案,這裡和大家介紹一個比上面兩個方案還要精簡的實現方案,只要560行程式碼且無需依賴於其他任何編檔案的實現。這個類是從Android系統通訊錄原始碼中提取的,類名為HanziToPinyin,其類檔案路徑如下:

/packages/providers/ContactsProvider/src/com/android/providers 
/contacts/HanziToPinyin.java

這是一個很獨立的類,需要使用的專案直接拷貝到自己對應的工程裡面即可使用,需要注意的是,我是在Android 4.2.2的系統原始碼中拷貝出來的,為什麼選擇4.2.2,一個是4.2.2之後(4.3開始)的HanziToPinyin不再可以獨立使用,需要依賴於Transliterator,而這個類我們是無法直接引用的。


而Android 2.x的HanziToPinyin在測試了很多轉換的結果發現是錯誤的,所以選擇了最後一個可以採納使用的版本Android 4.2.2。

如何使用

HanziToPinyin這個類的程式碼量非常少,結構也非常簡單

下面簡單的說明一下如何使用,非常簡單,只需要把需要轉換的漢字傳入get方法即可獲取返回的拼音結果



其返回的資料結構是一個HanziToPinyin.Token的ArrayList,HanziToPinyin.Token是HanziToPinyin中的一個公共靜態外部類,

其分別有type、source、target等三個成員變數,type是標識token的型別,有三種不同的取值1(拉丁文),2(拼音),3(未知),source是輸入的中文,target則是中文轉換後對應的拼音。這裡還有一個細節需要注意一下,只拷貝HanziToPinyin在原生系統上使用是沒有問題的,但是在國產手機的ROM上則無法正常使用,需要加上下面三行程式碼做適配:

否則HanziToPinyin的初始化狀態會設定錯誤,而導致無法實現漢字轉換成拼音。

內部實現

瞭解完如何使用後,我們來簡單窺探一下HanziToPinyin內部是如何實現的,先來看一下類中比較耀眼的兩個陣列UNIHANS和PINYINS(兩個類很長,截圖沒截全,大家自己看程式碼吧)



其中UNIHANS是一組漢字對應的unicode編碼,而PINYINS則是UNIHANS中每個元素對應的拼音的ASCII碼,如UNIHANS的第一個元素是\u963f,其對於的中文是,換成拼音則是A,而A對應的ASCII碼用十進位制表示則是65,對應的就是PINYINS的第一個陣列中的第一個元素,至於為什麼後面有5個0的元素,主要是因為漢字的拼音最長的有六個字母(例如:chuang),而只有一個a,所以後面的5個空位就需要用0來填充了。我們在呼叫get方法時將中文以String的形式傳入,方法內部會遍歷String中的每個元素,為其生成對應的Token,也就是我們最後拿到的那個ArrayList中的結果。


所以最關鍵的實現是在getToken方法中,這裡忽略getToken前面的30來行判斷程式碼,直接看關鍵部分

通過二分檢索的方式,使用java.text.Collator的compare方法不斷比對UNIHANS陣列中與輸入的漢字同音(注意:這裡是同音不是完全相同)的字,最終獲取其對應的在UNIHANS陣列中的下標位置offset。前面我們提到UNIHANS和PINYINS是相互對應的,所以這裡也能找到PINYINS中對應讀音的一組ASCII碼,通過int轉換成char,再使用StringBuilder進行拼接,就可以獲取對應的拼音了,實現思路上還是很簡單清晰的。


效能和不足

  • 在效能上,HanziToPinyin還是比較客觀的,畢竟用了二分檢索,在實際測試過程中丟了一篇5500多字的文章進行轉換,只用了415ms;
  • 在準確率上,拿了一堆人名和一個國家列表資料進行轉換,隨機抽取資料都沒有發現出錯的資料,但是按照這個類的實現上看,如果輸入的漢字拼音不與UNIHANS中任何一個元素同音,則必然無法得到正確的結果,實際測試中,我隨便拿了一些資料測試都沒有得到不正確的結果輸出,不知道得多生僻的字才能得出個錯誤結果;
  • HanziToPinyin這類並不支援多音字,所以如果一定要考慮多音字的問題,這個類就不適合了;

總結

關於HanziToPinyin就介紹到這裡,我已經將這個類的程式碼我已經整理放在Gist上( https://gist.github.com/D-clock/7a6e33f42c0177439a49d85b73f1e600 ),需要的同學自取 ,如果HanziToPinyin不能滿足你的需求,那可以考慮使用前面提到的pinyin4j和TinyPinyin。