JAVA如何正确处理Unicode字符
最近在開發(fā)輸入法程序時(shí)遇到一個(gè)小問題,就是刪除一個(gè)emoji時(shí),不能一次刪干凈,需要執(zhí)行兩次操作才可以。Intuitively,這肯定是java操作unicode字符的問題,于是找了JAVA官方文檔參考一下,解決了這個(gè)問題,這里做下簡單總結(jié)。原文在這里,有興趣自己看。
http://www.oracle.com/technetwork/articles/java/supplementary-142654.html
注:文章中提到的“JAVA字節(jié)”均指JAVA平臺的16位字節(jié),請不要和C的8位字節(jié)搞混。
首先需要知道標(biāo)準(zhǔn)Unicode字符是一個(gè)16位字符(廢話),所以標(biāo)準(zhǔn)Unicode字符集包含65536個(gè)字符(2的16次方嘛,哥的數(shù)學(xué)很好的)。但是僅僅65536個(gè)字符是遠(yuǎn)遠(yuǎn)不夠用的,尤其是為了包含我們偉大中華民族豐富多彩的文字(反正我是不認(rèn)識),Unicode字符集進(jìn)行了擴(kuò)展,擴(kuò)展到了24位,也就是最多可以包含1,112,064個(gè)字符。這里我們把標(biāo)準(zhǔn)Unicode字符集(也就是前65536個(gè)字符)叫做Basic Multilingual Plane (BMP),把超過16位以上的擴(kuò)展字符集叫做supplementary characters。
UTF-16是一種編碼方式,以16位無符號單元來編碼Unicode字符,如果對一個(gè)標(biāo)準(zhǔn)Unicode字符編碼,只需要占用一個(gè)UTF-16單元,如果對擴(kuò)展Unicode字符編碼則需要占用兩個(gè)UTF-16單元。
我們都知道在C語言中,一個(gè)primitive char占用一個(gè)字節(jié),也就是8位。但是在JAVA中,一個(gè)primitive char占用16位,與一個(gè)標(biāo)準(zhǔn)Unicode字符長度相等,因?yàn)镴AVA平臺采用UTF-16進(jìn)行編碼。這樣JAVA就很容易處理Unicode基本字符。但是對于Unicode的擴(kuò)展字符,在JAVA中就需要占用兩個(gè)char,也就是兩個(gè)UTF-16單元。
舉個(gè)栗子,大寫字母A的Unicode值為U+0041,它屬于Unicode的基本字符集,所以它只占用一個(gè)UTF-16單元,表示為[0041]。而字符的Unicode值為U+10400,它屬于擴(kuò)展Unicode字符集,所以它占用兩個(gè)UTF-16編碼單元,表示為[D801][DC00](一個(gè)中括號代表一個(gè)UTF-16單元,中括號本身沒意義)。第一個(gè)單元叫做high-surrogates,范圍從?U+D800 到 U+DBFF,第二個(gè)單元叫l(wèi)ow-surrogates,范圍從 U+DC00 到 U+DFFF,這個(gè)看起來很類似多字節(jié)編碼。但是,我說的是但是,這里有一個(gè)非常重要的不同點(diǎn),從U+D800 到 U+DFFF 其實(shí)為UTF-16的保留值范圍,專門用作編碼Unicode擴(kuò)展字符集,這個(gè)范圍不被賦予任何實(shí)際的標(biāo)準(zhǔn)Unicode字符。也就是說,在你的程序里只要判斷一個(gè)字符是否屬于這個(gè)范圍內(nèi),就可以知道這個(gè)字符是應(yīng)該被當(dāng)做一個(gè)獨(dú)立的Unicode基本字符處理,還是當(dāng)做半個(gè)擴(kuò)展Unicode字符。
回到我一開始提到問題,我在開發(fā)android輸入法的時(shí)候,當(dāng)需要?jiǎng)h除一個(gè)emoji時(shí),總是需要?jiǎng)h除兩次才能刪干凈。這是因?yàn)槲矣玫膃moji都是屬于Unicode擴(kuò)展字符集的,在編輯框中占用了兩個(gè)UTF-16單元,而每執(zhí)行一次刪除操作只刪除了一個(gè)UTF-16單元,也就是一個(gè)JAVA字節(jié),而另一個(gè)單元沒有被刪除,所以就會顯示異常。這時(shí)候只要再刪除掉另一個(gè)字節(jié)就算真正把這個(gè)emoji刪除干凈了。
用戶在實(shí)際的應(yīng)用中,肯定是把Unicode標(biāo)準(zhǔn)字符集和擴(kuò)展字符集混合著使用,也就是說有的字符占用1個(gè)java字節(jié),有的字符占用兩個(gè)java字節(jié)。那么我在執(zhí)行刪除一個(gè)字符的操作時(shí)必須也要先去判斷我是要一次性刪除一個(gè)字節(jié)還是刪除兩個(gè)字節(jié)。這就很簡單了,按照我前面提到的規(guī)則,先判斷一下被操作的字符值是否處于[U+D800,U+DFFF](inclusive)之間,如果是,就說明被操作的字符不是一個(gè)有效的標(biāo)準(zhǔn)Unicode字符,而是半個(gè)Unicode擴(kuò)展字符,那么只需要在程序中自動刪除兩個(gè)java字節(jié)就可以了。反之,如果被處理的字符不屬于[U+D800,U+DFFF]范圍內(nèi),那么這個(gè)字符就是一個(gè)標(biāo)準(zhǔn)的Unicode字符,只需要按照一個(gè)java字節(jié)處理就可以了。
理論清楚后(我假設(shè)你清楚了,其實(shí)也不難,就是我表達(dá)的不清楚,沒辦法,工科人,總是思維超越語言),我再簡單介紹一下java中相關(guān)的處理函數(shù)。
Character.toChars(int?codePoint):參數(shù)codePoint為Unicode值,此函數(shù)將Unicode值轉(zhuǎn)換為標(biāo)準(zhǔn)java字節(jié)數(shù)組。如果codePoint是Unicode標(biāo)準(zhǔn)字符,則返回值只包含一個(gè)char;如果codePoint是擴(kuò)展Unicode字符,則返回值包含2個(gè)char。
例:在程序中如果要輸出一個(gè)Unicode擴(kuò)展字符,可以這樣String.valueOf(Character.toChars(0x1F60E))
Character.isLowSurrogate(char?ch):判斷一個(gè)字符是否是一個(gè)Unicode擴(kuò)展字符的低16位編碼。
Character.isHighSurrogate(char?ch):判斷一個(gè)字符是否是一個(gè)Unicode擴(kuò)展字符的高16位編碼。
轉(zhuǎn)載于:https://blog.51cto.com/yaorugang/1704142
總結(jié)
以上是生活随笔為你收集整理的JAVA如何正确处理Unicode字符的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ICE相关链接
- 下一篇: fgui的ui管理框架_ET框架FGUI