Java代碼點和代碼單元

這個寫的最好 http://blog.csdn.net/x_iya/article/details/53130117

這些應該是從Unicode標準而來的術語兔毙,Unicode標準的核心是一個編碼字符集,它為每一個字符分配一個唯一數(shù)字晚胡。Unicode標準始終使用16進制數(shù)字筏养,并且在書寫時在前面加上U+际长,如字符“A”的編碼為“U+0041”餐屎。

代碼點是指可用于編碼字符集的數(shù)字场靴。編碼字符集定義一個有效的代碼點范圍尿庐,但是并不一定將字符分配給所有這些代碼點忠怖。有效的Unicode代碼點范圍是U+0000至U+10FFFF。Unicode4.0將字符分配給一百多萬個代碼點中的96382個代碼點抄瑟。
代碼單元可以理解為字符編碼的一個基本單元凡泣,最常用的代碼單元是字節(jié)(即8位),但是16位和32位整數(shù)也可以用于內部處理皮假。

就Unicode標準來說鞋拟,它的編碼字符集有三種編碼方案:UTF-32、UTF-16惹资、UTF-8贺纲。UTF-32使用32位的代碼單元表示一個Unicode代碼點,UTF-16使用一個或兩個16位的代碼單元的序列對Unicode代碼點進行編碼布轿,UTF-8 使用1至4個字節(jié)的序列對Unicode代碼點進行編碼哮笆。

摘要
本文介紹 Java 平臺支持增補字符的方式。增補字符是 Unicode 標準中代碼點超出 U+FFFF 的字符汰扭,因此它們無法在 Java 編程語言中描述為單個的 16 位實體(例如char數(shù)據(jù)類型)稠肘。這些字符一般極少用,但是萝毛,有些會在諸如中文或日文人名中用到项阴,因此,在東亞國家笆包,政府應用程序通常會要求支持這些字符环揽。
Java 平臺目前正在改進,以便支持對增補字符的處理庵佣,這種改進對現(xiàn)有的應用程序影響微乎其微歉胶。新的低層 API 在需要時能夠使用單個的字符運行。不過巴粪,大多數(shù)文本處理 API 均使用字符序列通今,例如String類或字符數(shù)組。現(xiàn)在肛根,這些均解釋為 UTF-16 序列辫塌,而且,這些 API 實現(xiàn)已轉變?yōu)檎_地處理增補字符派哲。這些改進已融入 Java 2 平臺 5.0 版臼氨,標準版 (J2SE)。
除詳細解釋這些改進之外芭届,本文同時為應用程序開發(fā)人員確定和實現(xiàn)必要的更改提供指導储矩,以支持整個 Unicode 字符集的使用感耙。
** 背景**
Unicode 最初設計是作為一種固定寬度的 16 位字符編碼。在 Java 編程語言中椰苟,基本數(shù)據(jù)類型char初衷是通過提供一種簡單的抑月、能夠包含任何字符的數(shù)據(jù)類型來充分利用這種設計的優(yōu)點。不過舆蝴,現(xiàn)在看來谦絮,16 位編碼的所有 65,536 個字符并不能完全表示全世界所有正在使用或曾經(jīng)使用的字符洁仗。于是层皱,Unicode 標準已擴展到包含多達 1,112赠潦,064 個字符叫胖。那些超出原來的 16 位限制的字符被稱作增補字符。Unicode 標準 2.0 版是第一個包含啟用增補字符設計的版本她奥,但是瓮增,直到 3.1 版才收入第一批增補字符集。由于 J2SE 的 5.0 版必須支持 Unicode 標準 4.0 版哩俭,因此它必須支持增補字符绷跑。
對增補字符的支持也可能會成為東亞市場的一個普遍商業(yè)要求。政府應用程序會需要這些增補字符凡资,以正確表示一些包含罕見中文字符的姓名砸捏。出版應用程序可能會需要這些增補字符,以表示所有的古代字符和變體字符隙赁。中國政府要求支持 GB18030(一種對整個 Unicode 字符集進行編碼的字符編碼標準)垦藏,因此,如果是 Unicode 3.1 版或更新版本伞访,則將包括增補字符掂骏。臺灣標準 CNS-11643 包含的許多字符在 Unicode 3.1 中列為增補字符。香港政府定義了一種針對粵語的字符集厚掷,其中的一些字符是 Unicode 中的增補字符弟灼。最后,日本的一些供應商正計劃利用增補字符空間中大量的專用空間收入 50蝗肪,000 多個日文漢字字符變體袜爪,以便從其專有系統(tǒng)遷移至基于 Java 平臺的解決方案蠕趁。
因此薛闪,Java 平臺不僅需要支持增補字符,而且必須使應用程序能夠方便地做到這一點俺陋。由于增補字符打破了 Java 編程語言的基礎設計構想豁延,而且可能要求對編程模型進行根本性的修改昙篙,因此,Java Community Process 召集了一個專家組诱咏,以期找到一個適當?shù)慕鉀Q方案苔可。該小組被稱為 JSR-204 專家組,使用Unicode 增補字符支持的 Java 技術規(guī)范請求的編號袋狞。從技術上來說焚辅,該專家組的決定僅適用于 J2SE 平臺,但是由于 Java 2 平臺企業(yè)版 (J2EE) 處于 J2SE 平臺的最上層苟鸯,因此它可以直接受益同蜻,我們期望 Java 2 平臺袖珍版 (J2ME) 的配置也采用相同的設計方法。
不過早处,在了解 JSR-204 專家組確定的解決方案之前湾蔓,我們需要先理解一些術語。
代碼點砌梆、字符編碼方案默责、UTF-16:這些是指什么?
不幸的是咸包,引入增補字符使字符模型變得更加復雜了桃序。在過去,我們可以簡單地說“字符”诉儒,在一個基于 Unicode 的環(huán)境(例如 Java 平臺)中葡缰,假定字符有 16 位,而現(xiàn)在我們需要更多的術語忱反。我們會盡量介紹得相對簡單一些 — 如需了解所有詳細的討論信息泛释,您可以閱讀Unicode 標準第 2 章或 Unicode 技術報告 17“字符編碼模型”。Unicode 專業(yè)人士可略過所有介紹直接參閱本部分中的最后定義温算。
字符是抽象的最小文本單位怜校。它沒有固定的形狀(可能是一個字形),而且沒有值注竿∏炎拢“A”是一個字符,“€”(德國巩割、法國和許多其他歐洲國家通用貨幣的標志)也是一個字符裙顽。
字符集是字符的集合。例如宣谈,漢字字符是中國人最先發(fā)明的字符愈犹,在中文、日文、韓文和越南文的書寫中使用漩怎。
編碼字符集是一個字符集勋颖,它為每一個字符分配一個唯一數(shù)字。Unicode 標準的核心是一個編碼字符集勋锤,字母“A”的編碼為 004116 和字符“€”的編碼為20AC16.Unicode 標準始終使用十六進制數(shù)字饭玲,而且在書寫時在前面加上前綴“U+”,所以“A”的編碼書寫為“U+0041”叁执。
代碼點是指可用于編碼字符集的數(shù)字茄厘。編碼字符集定義一個有效的代碼點范圍,但是并不一定將字符分配給所有這些代碼點谈宛。有效的 Unicode 代碼點范圍是 U+0000 至 U+10FFFF.Unicode 4.0 將字符分配給一百多萬個代碼點中的 96蚕断,382 代碼點。
增補字符是代碼點在 U+10000 至 U+10FFFF 范圍之間的字符入挣,也就是那些使用原始的 Unicode 的 16 位設計無法表示的字符亿乳。從 U+0000 至 U+FFFF 之間的字符集有時候被稱為基本多語言面 (BMP)。因此径筏,每一個 Unicode 字符要么屬于 BMP葛假,要么屬于增補字符。
字符編碼方案是從一個或多個編碼字符集到一個或多個固定寬度代碼單元序列的映射滋恬。最常用的代碼單元是字節(jié)聊训,但是 16 位或 32 位整數(shù)也可用于內部處理。UTF-32恢氯、UTF-16 和 UTF-8 是 Unicode 標準的編碼字符集的字符編碼方案带斑。
UTF-32 即將每一個 Unicode 代碼點表示為相同值的 32 位整數(shù)。很明顯勋拟,它是內部處理最方便的表達方式勋磕,但是,如果作為一般字符串表達方式敢靡,則要消耗更多的內存挂滓。
UTF-16 使用一個或兩個未分配的 16 位代碼單元的序列對 Unicode 代碼點進行編碼。值 U+0000 至 U+FFFF 編碼為一個相同值的 16 位單元啸胧。增補字符編碼為兩個代碼單元赶站,第一個單元來自于高代理范圍(U+D800 至 U+DBFF),第二個單元來自于低代理范圍(U+DC00 至 U+DFFF)纺念。這在概念上可能看起來類似于多字節(jié)編碼贝椿,但是其中有一個重要區(qū)別:值 U+D800 至 U+DFFF 保留用于 UTF-16;沒有這些值分配字符作為代碼點陷谱。這意味著烙博,對于一個字符串中的每個單獨的代碼單元,軟件可以識別是否該代碼單元表示某個單單元字符,或者是否該代碼單元是某個雙單元字符的第一個或第二單元习勤。這相當于某些傳統(tǒng)的多字節(jié)字符編碼來說是一個顯著的改進,在傳統(tǒng)的多字節(jié)字符編碼中焙格,字節(jié)值 0x41 既可能表示字母“A”图毕,也可能是一個雙字節(jié)字符的第二個字節(jié)。
UTF-8 使用一至四個字節(jié)的序列對編碼 Unicode 代碼點進行編碼眷唉。U+0000 至 U+007F 使用一個字節(jié)編碼予颤,U+0080 至 U+07FF 使用兩個字節(jié),U+0800 至 U+FFFF 使用三個字節(jié)冬阳,而 U+10000 至 U+10FFFF 使用四個字節(jié)蛤虐。UTF-8 設計原理為:字節(jié)值 0x00 至 0x7F 始終表示代碼點 U+0000 至 U+007F(Basic Latin 字符子集,它對應 ASCII 字符集)肝陪。這些字節(jié)值永遠不會表示其他代碼點驳庭,這一特性使 UTF-8 可以很方便地在軟件中將特殊的含義賦予某些 ASCII 字符。
下表所示為幾個字符不同表達方式的比較:

Unicode 代碼點

U+0041

U+00DF

U+6771

U+10400

表示字形

UTF-32 代碼單元

00000041

000000DF

00006771

00010400

UTF-16 代碼單元

0041

00DF

6771

D801

DC00

UTF-8 代碼單元

41

C3

9F

E6

9D

B1

F0

90

90

80

另外氯窍,本文在許多地方使用術語字符序列或char序列概括 Java 2 平臺識別的所有字符序列的容器:char[]饲常, java.lang.CharSequence的實現(xiàn)(例如String類),和java.text.CharacterIterator的實現(xiàn)狼讨。
這么多術語贝淤。它們與在 Java 平臺中支持增補字符有什么關系呢?

Java 平臺中增補字符的設計方法
JSR-204 專家組必須作出的主要決定是如何在 Java API 中表示增補字符政供,包括單個字符和所有形式的字符序列播聪。專家組考慮并排除了多種方法:
重新定義基本類型char,使其具有 32 位布隔,這樣也會使所有形式的char序列成為 UTF-32 序列离陶。 在現(xiàn)有的 16 位類型char的基礎上,為字符引入一種新的 32 位基本類型(例如衅檀,char32)枕磁。所有形式的 Char 序列均基于 UTF-16. 在現(xiàn)有的 16 位類型char的基礎上,為字符引入一種新的 32 位基本類型(例如术吝,char32)计济。String和StringBuffer接受并行 API,并將它們解釋為 UTF-16 序列或 UTF-32 序列排苍;其他char序列繼續(xù)基于 UTF-16. 使用int表示增補的代碼點沦寂。String和StringBuffer接受并行 API,并將它們解釋為 UTF-16 序列或 UTF-32 序列淘衙;其他char序列繼續(xù)基于 UTF-16. 使用代理char對传藏,表示增補代碼點。所有形式的char序列基于 UTF-16. 引入一種封裝字符的類。String和StringBuffer接受新的 API毯侦,并將它們解釋為此類字符的序列哭靖。 使用一個CharSequence實例和一個索引的組合表示代碼點。
在這些方法中侈离,一些在早期就被排除了试幽。例如,重新定義基本類型char卦碾,使其具有 32 位铺坞,這對于全新的平臺可能會非常有吸引力,但是洲胖,對于 J2SE 來說济榨,它會與現(xiàn)有的 Java 虛擬機1、序列化和其他接口不兼容绿映,更不用說基于 UTF-32 的字符串要使用兩倍于基于 UTF-16 的字符串的內存了擒滑。添加一種新類型的char32可能會簡單一些,但是仍然會出現(xiàn)虛擬機和序列化方面的問題叉弦。而且橘忱,語言更改通常需要比 API 更改有更長的提前期,因此卸奉,前面兩種方法會對增補字符支持帶來無法接受的延遲钝诚。為了在余下的方法中篩選出最優(yōu)方案,實現(xiàn)小組使用四種不同的方法榄棵,在大量進行低層字符處理的代碼(java.util.regex包)中實現(xiàn)了對增補字符支持凝颇,并對這四種方法的難易程度和運行表現(xiàn)進行了比較。
最終疹鳄,專家組確定了一種分層的方法:
使用基本類型int在低層 API 中表示代碼點拧略,例如Character類的靜態(tài)方法。 將所有形式的char序列均解釋為 UTF-16 序列瘪弓,并促進其在更高層級 API 中的使用垫蛆。 提供 API,以方便在各種char和基于代碼點的表示法之間的轉換腺怯。
在需要時袱饭,此方法既能夠提供一種概念簡明且高效的單個字符表示法,又能夠充分利用通過改進可支持增補字符的現(xiàn)有 API.同時呛占,還能夠促進字符序列在單個字符上的應用虑乖,這一點一般對于國際化的軟件很有好處。
在這種方法中晾虑,一個char表示一個 UTF-16 代碼單元疹味,這樣對于表示代碼點有時并不夠用仅叫。您會注意到,J2SE 技術規(guī)范現(xiàn)在使用術語代碼點和 UTF-16 代碼單元(表示法是相關的)以及通用術語字符(表示法與該討論沒有關系)糙捺。API 通常使用名稱codePoint描述表示代碼點的類型int的變量诫咱,而 UTF-16 代碼單元的類型當然為char.我們將在下面兩部分中了解到 J2SE 平臺的實質變化 — 其中一部分介紹單個代碼點的低層 API,另一部分介紹采用字符序列的高層接口洪灯。
開放的增補字符:基于代碼點的 API
新增的低層 API 分為兩大類:用于各種char和基于代碼點的表示法之間轉換的方法和用于分析和映射代碼點的方法坎缭。
最基本的轉換方法是Character.toCodePoint(char high, char low)(用于將兩個 UTF-16 代碼單元轉換為一個代碼點)和Character.toChars(int codePoint)(用于將指定的代碼點轉換為一個或兩個 UTF-16 代碼單元婴渡,然后封裝到一個char[]內。不過凯亮,由于大多數(shù)情況下文本以字符序列的形式出現(xiàn)边臼,因此,另外提供codePointAt和codePointBefore方法假消,用于將代碼點從各種字符序列表示法中提取出來:Character.codePointAt(char[] a柠并, int index)和String.codePointBefore(int index)是兩種典型的例子。在將代碼點插入字符序列時富拗,大多數(shù)情況下均有一些針對StringBuffer和StringBuilder類的appendCodePoint(int codePoint)方法臼予,以及一個用于提取表示代碼點的int[]的String構建器。
幾種用于分析代碼單元和代碼點的方法有助于轉換過程:Character 類中的isHighSurrogate和isLowSurrogate方法可以識別用于表示增補字符的char值啃沪;charCount(int codePoint)方法可以確定是否需要將某個代碼點轉換為一個或兩個char.但是粘拾,大多數(shù)基于代碼點的方法均能夠對所有 Unicode 字符實現(xiàn)基于char的舊方法對 BMP 字符所實現(xiàn)的功能。以下是一些典型例子:
Character.isLetter(int codePoint)可根據(jù) Unicode 標準識別字母创千。 Character.isJavaIdentifierStart(int codePoint)可根據(jù) Java 語言規(guī)范確定代碼點是否可以啟動標識符缰雇。 Character.UnicodeBlock.of(int codePoint)可搜索代碼點所屬的 Unicode 字符子集。 Character.toUpperCase(int codePoint)可將給定的代碼點轉換為其大寫等值字符追驴。盡管此方法能夠支持增補字符械哟,但是它仍然不能解決根本的問題,即在某些情況下殿雪,逐個字符的轉換無法正確完成暇咆。例如,德文字符“"丙曙?"”應該轉換為“SS”爸业,這需要使用String.toUpperCase方法。
注意大多數(shù)接受代碼點的方法并不檢查給定的int值是否處于有效的 Unicode 代碼點范圍之內(如上所述亏镰,只有 0x0 至 0x10FFFF 之間的范圍是有效的)沃呢。在大多數(shù)情況下,該值是以確保其有效的方法產生的拆挥,在這些低層 API 中反復檢查其有效性可能會對系統(tǒng)性能造成負面的影響薄霜。在無法確保有效性的情況下某抓,應用程序必須使用Character.isValidCodePoint方法確保代碼點有效。大多數(shù)方法對于無效的代碼點采取的行為沒有特別加以指定惰瓜,不同的實現(xiàn)可能會有所不同否副。
API 包含許多簡便的方法,這些方法可使用其他低層的 API 實現(xiàn)崎坊,但是專家組覺得备禀,這些方法很常用,將它們添加到 J2SE 平臺上很有意義奈揍。不過曲尸,專家組也排除了一些建議的簡便方法,這給我們提供了一次展示自己實現(xiàn)此類方法能力的機會男翰。例如另患,專家組經(jīng)過討論,排除了一種針對String類的新構建器(該構建器可以創(chuàng)建一個保持單個代碼點的String)蛾绎。以下是使應用程序使用現(xiàn)有的 API 提供功能的一種簡便方法:

/*** 創(chuàng)建僅含有指定代碼點的新 String. */ String newString(int codePoint) { return new String(Character.toChars(codePoint))昆箕;}

您會注意到,在這個簡單的實現(xiàn)中租冠,toChars方法始終創(chuàng)建一個中間數(shù)列鹏倘,該數(shù)列僅使用一次即立即丟棄。如果該方法在您的性能評估中出現(xiàn)顽爹,您可能會希望將其優(yōu)化為針對最為普通的情況纤泵,即該代碼點為 BMP 字符:

/*** 創(chuàng)建僅含有指定代碼點的新 String.* 針對 BMP 字符優(yōu)化的版本。 */ String newString(int codePoint) { if (Character.charCount(codePoint) == 1) { return String.valueOf((char) codePoint)镜粤; } else { return new String(Character.toChars(codePoint))夕吻; }}

或者,如果您需要創(chuàng)建許多個這樣的 string繁仁,則可能希望編寫一個重復使用toChars方法所使用的數(shù)列的通用版本:

/*** 創(chuàng)建每一個均含有一個指定* 代碼點的新 String. * 針對 BMP 字符優(yōu)化的版本涉馅。 */ String[] newStrings(int[] codePoints) { String[] result = new String[codePoints.length]; char[] codeUnits = new char[2]黄虱; for (int i = 0稚矿; i < codePoints.length; i++) { int count = Character.toChars(codePoints[i]捻浦, codeUnits晤揣, 0); result[i] = new String(codeUnits朱灿, 0昧识, count); } return result盗扒;}

不過跪楞,最終您可能會發(fā)現(xiàn)缀去,您需要的是一個完全不同的解決方案。新的構建器String(int codePoint)實際上建議作為String.valueOf(char)的一個基于代碼點的備選方案甸祭。在很多情況下缕碎,此方法用于消息生成的環(huán)境,例如:

System.out.println("Character " + String.valueOf(char) + " is invalid.")池户;

新的格式化 API支持增補文字咏雌,提供一種更加簡單的備選方案:

System.out.printf("Character %c is invalid.%n", codePoint)校焦;

使用此高層 API 不僅簡捷赊抖,而它有很多特殊的優(yōu)點:它可以避免串聯(lián)(串聯(lián)會使消息很難本地化),并將需要移進資源包 (resource bundle) 的字符串數(shù)量從兩個減少到一個寨典。

增補字符透視:功能增強
在支持使用增補字符的 Java 2 平臺中的大部分更改沒有反映到新的 API 內氛雪。一般預期是,處理字符序列的所有接口將以適合其功能的方式處理增補字符凝赛。本部分著重講述為達到此預期所作一些功能增強注暗。
Java 編程語言中的標識符
Java 語言規(guī)范指出所有 Unicode 字母和數(shù)字均可用于標識符坛缕。許多增補字符是字母或數(shù)字墓猎,因此 Java 語言規(guī)范已經(jīng)參照新的基于代碼點的方法進行更新,以在標識符內定義合法字符赚楚。為使用這些新方法毙沾,需要檢測標識符的 javac 編譯器和其他工具都進行了修訂。
** 庫內的增補字符支持**
許多 J2SE 庫已經(jīng)過增強宠页,可以通過現(xiàn)有接口支持增補字符左胞。以下是一些例子:
字符串大小寫轉換功能已更新,可以處理增補字符举户,也可以實現(xiàn) Unicode 標準中規(guī)定的特殊大小寫規(guī)則烤宙。 java.util.regex包已更新,這樣模式字符串和目標字符串均可以包含增補字符并將其作為完整單元處理俭嘁。 現(xiàn)在躺枕,在java.text包內進行整理處理時,會將增補字符看作完整單元供填。 java.text.Bidi類已更新拐云,可以處理增補字符和 Unicode 4.0 中新增的其他字符。請注意近她,Cypriot Syllabary 字符子集內的增補字符具有從右至左的方向性叉瘩。 Java 2D API 內的字體渲染和打印技術已經(jīng)過增強,可以正確渲染和測量包含增補字符的字符串粘捎。 Swing 文本組件實現(xiàn)已更新薇缅,可以處理包含增補字符的文本危彩。
字符轉換
只有很少的字符編碼可以表示增補字符。如果是基于 Unicode 的編碼(如 UTF-8 和 UTF-16LE)捅暴,則舊版的 J2RE 內的字符轉換器已經(jīng)按照正確處理增補字符的方式實現(xiàn)轉換恬砂。對于 J2RE 5.0,可以表示增補字符的其他編碼的轉換器已更新:GB18030蓬痒、x-EUC-TW(現(xiàn)在實現(xiàn)所有 CNS 11643 層面)和 Big5-HKSCS(現(xiàn)在實現(xiàn) HKSCS-2001)泻骤。
在源文件內表示增補字符
在 Java 編程語言源文件中,如果使用可以直接表示增補字符的字符編碼梧奢,則使用增補字符最為方便狱掂。UTF-8 是最佳的選擇。在所使用的字符編碼無法直接表示字符的情況下亲轨,Java 編程語言提供一種 Unicode 轉義符語法趋惨。此語法沒有經(jīng)過增強,無法直接表示增補字符惦蚊。而是使用兩個連續(xù)的 Unicode 轉義符將其表示為 UTF-16 字符表示法中的兩個編碼單元器虾。例如,字符 U+20000 寫作“/uD840/uDC00”蹦锋。您也許不愿意探究這些轉義序列的含義兆沙;最好是寫入支持所需增補字符的編碼,然后使用一種工具(如 native2ascii)將其轉換為轉義序列莉掂。
遺憾的是葛圃,由于其編碼問題,屬性文件仍局限于 ISO 8859-1(除非您的應用程序使用新的 XML 格式)憎妙。這意味著您始終必須對增補字符使用轉義序列库正,而且可能要使用不同的編碼進行編寫,然后使用諸如 native2ascii 的工具進行轉換厘唾。
** 經(jīng)修訂的 UTF-8**
Java 平臺對經(jīng)修訂的 UTF-8 已經(jīng)很熟悉褥符,但是,問題是應用程序開發(fā)人員在可能包含增補字符的文本和 UTF-8 之間進行轉換時需要更加留神抚垃。需要特別注意的是喷楣,某些 J2SE 接口使用的編碼與 UTF-8 相似但與其并不兼容。以前讯柔,此編碼有時被稱為“Java modified UTF-8”(經(jīng) Java 修訂的 UTF-8)或(錯誤地)直接稱為“UTF-8”抡蛙。對于 J2SE 5.0,其說明文檔正在更新魂迄,此編碼將統(tǒng)稱為“modified UTF-8”(經(jīng)修訂的 UTF-8)粗截。
經(jīng)修訂的 UTF-8 和標準 UTF-8 之間之所以不兼容,其原因有兩點捣炬。其一熊昌,經(jīng)修訂的 UTF-8 將字符 U+0000 表示為雙字節(jié)序列 0xC0 0x80绽榛,而標準 UTF-8 使用單字節(jié)值 0x0.其二,經(jīng)修訂的 UTF-8 通過對其 UTF-16 表示法的兩個代理代碼單元單獨進行編碼表示增補字符婿屹。每個代理代碼單元由三個字節(jié)來表示灭美,共有六個字節(jié)。而標準 UTF-8 使用單個四字節(jié)序列表示整個字符昂利。
Java 虛擬機及其附帶的接口(如 Java 本機接口届腐、多種工具接口或 Java 類文件)在java.io.DataInput和DataOutput接口和類中使用經(jīng)修訂的 UTF-8 實現(xiàn)或使用這些接口和類,并進行序列化蜂奸。Java 本機接口提供與經(jīng)修訂的 UTF-8 之間進行轉換的例程犁苏。而標準 UTF-8 由String類、java.io.InputStreamReader和OutputStreamWriter類扩所、java.nio.charset設施 (facility) 以及許多其上層的 API 提供支持围详。
由于經(jīng)修訂的 UTF-8 與標準的 UTF-8 不兼容,因此切勿同時使用這兩種版本的編碼祖屏。經(jīng)修訂的 UTF-8 只能與上述的 Java 接口配合使用助赞。在任何其他情況下,尤其對于可能來自非基于 Java 平臺的軟件的或可能通過其編譯的數(shù)據(jù)流袁勺,必須使用標準的 UTF-8.需要使用標準的 UTF-8 時雹食,則不能使用 Java 本機接口例程與經(jīng)修訂的 UTF-8 進行轉換。
在應用程序內支持增補字符
現(xiàn)在魁兼,對大多數(shù)讀者來說最為重要的問題是:必須對應用程序進行哪些更改才能支持增補字符婉徘?
答案取決于在應用程序中進行哪種類型的文本處理和使用哪些 Java 平臺 API.對于僅以各種形式char序列([char[]漠嵌、java.lang.CharSequence實現(xiàn)咐汞、java.text.CharacterIterator實現(xiàn))處理文本和僅使用接受和退回序列(如char序列)的 Java API 的應用程序,可能根本不需要進行任何更改儒鹿。Java 平臺 API 的實現(xiàn)應該能夠處理增補字符化撕。
對于本身解釋單個字符、將單個字符傳送給 Java 平臺 API 或調用能夠返回單個字符的方法的應用程序约炎,則需要考慮這些字符的有效值植阴。在很多情況下,往往不要求支持增補字符圾浅。例如掠手,如果某應用程序搜索char序列中的 HTML 標記,并逐一檢查每個char狸捕,它會知道這些標記僅使用 Basic Latin 字符子集中的字符喷鸽。如果所搜索的文本含有增補字符,則這些字符不會與標記字符混淆灸拍,因為 UTF-16 使用代碼單元表示增補字符做祝,而代碼單元的值不會用于 BMP 字符砾省。
只有在某應用程序本身解釋單個字符、將單個字符傳送給 Java 平臺 API 或調用能夠返回單個字符的方法且這些字符可能為增補字符時混槐,才必須更改該應用程序编兄。在提供使用char序列的并行 API 時,最好轉而使用此類 API.在其他情況下声登,有必要使用新的 API 在char和基于代碼點的表示法之間進行轉換狠鸳,并調用基于代碼點的 API.當然,如果您發(fā)現(xiàn)在 J2SE 5.0 中有更新悯嗓、更方便的 API碰煌,使您能夠支持增補字符并同時簡化代碼(如上格式化范例中所述),則沒有必要這樣做绅作。
您可能會猶豫芦圾,是將所有文本轉換為代碼點表示法(即int[])然后在該表示法中處理,還是在大多數(shù)情況下仍采用char序列俄认,僅在需要時轉換為代碼點个少,兩者之間孰優(yōu)孰劣很難確定。當然眯杏,總體來說夜焦,Java 平臺 API 相對于char序列肯定具有一定的優(yōu)勢,而且采用 Java 平臺 API 可以節(jié)省內存空間岂贩。
對于需要與 UTF-8 之間進行轉換的應用程序茫经,還需要認真考慮是需要標準的 UTF-8 還是經(jīng)修訂的 UTF-8,并針對每種 UTF-8 采用適當?shù)?Java 平臺萎津⌒渡。“經(jīng)修訂的 UTF-8”部分介紹進行正確選擇所需的信息。
使用增補字符測試應用程序
經(jīng)過前面部分的介紹后锉屈,無論您是否需要修訂應用程序荤傲,測試應用程序是否運行正常始終是一種正確的做法。對于不含有圖形用戶界面的應用程序颈渊,有關“在源文件內表示增補字符” 的信息有助于設計測試用例遂黍。以下是有關使用圖形用戶界面進行測試的補充信息。
對于文本輸入俊嗽,Java 2 SDK提供用于接受“/Uxxxxxx”格式字符串的代碼點輸入方法雾家,這里大寫的“U”表示轉義序列包含六個十六進制數(shù)字,因此允許使用增補字符绍豁。小寫的“u”表示轉義序列“/uxxxx”的原始格式芯咧。您可以在 J2SDK 目錄 demo/jfc/CodePointIM 內找到此輸入方法及其說明文檔。
對于字體渲染,您需要至少能夠渲染一些增補字符的字體唬党。其中一種此類字體為 James Kass 的Code2001字體鹃共,它提供手寫體字形(如 Deseret 和 Old Italic)。利用 Java 2D 庫中提供新功能驶拱,您只需將該字體安裝到 J2RE 的 lib/fonts/fallback 目錄內即可霜浴,然后它可自動添加至在 2D 和 XAWT 渲染時使用的所有邏輯字體 — 無需編輯字體配置文件。
至此蓝纲,您就可以確認阴孟,您的應用程序能夠完全支持增補字符了!
結論
對增補字符的支持已經(jīng)引入 Java 平臺税迷,大部分應用程序無需更改代碼即可處理這些字符永丝。解釋單個字符的應用程序可以在Character類和多種CharSequence子類中使用基于代碼點的新 API.
以下是Unicode和UTF-8之間的轉換關系表:

U-00000000 - U-0000007F: 0xxxxxxx U-00000080 - U-000007FF: 110xxxxx 10xxxxxx U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

**Byte 數(shù)組轉整數(shù):**

static int bytes2int(byte[] b) { int mask=0xff; int temp=0箭养; int res=0慕嚷; for(int i=0;i<4毕泌;i++){ res<<=8喝检; temp=b[i]&mask; res|=temp撼泛; } return res挠说;}
整數(shù)轉byte數(shù)組:
static byte[] int2bytes(int num) { byte[] b=new byte[4]; int mask=0xff愿题; for(int i=0慧起;i<4曹货;i++){ b[i]=(byte)(num>>>(24-i*8))亮垫; } return b揪阿;}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市崎脉,隨后出現(xiàn)的幾起案子拧咳,更是在濱河造成了極大的恐慌伯顶,老刑警劉巖囚灼,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異祭衩,居然都是意外死亡灶体,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門掐暮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝎抽,“玉大人,你說我怎么就攤上這事≌两幔” “怎么了养交?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瓢宦。 經(jīng)常有香客問我碎连,道長,這世上最難降的妖魔是什么驮履? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任鱼辙,我火速辦了婚禮,結果婚禮上玫镐,老公的妹妹穿的比我還像新娘倒戏。我一直安慰自己,他們只是感情好恐似,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布杜跷。 她就那樣靜靜地躺著,像睡著了一般矫夷。 火紅的嫁衣襯著肌膚如雪葱椭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天口四,我揣著相機與錄音孵运,去河邊找鬼。 笑死蔓彩,一個胖子當著我的面吹牛治笨,可吹牛的內容都是我干的。 我是一名探鬼主播赤嚼,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼旷赖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了更卒?” 一聲冷哼從身側響起等孵,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蹂空,沒想到半個月后俯萌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡上枕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年咐熙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辨萍。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡棋恼,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情爪飘,我是刑警寧澤义起,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站师崎,受9級特大地震影響并扇,放射性物質發(fā)生泄漏。R本人自食惡果不足惜抡诞,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一穷蛹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昼汗,春花似錦肴熏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鞋吉,卻和暖如春鸦做,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谓着。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工泼诱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赊锚。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓治筒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舷蒲。 傳聞我的和親對象是個殘疾皇子耸袜,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容