Java中的代码点和代码单元
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
這篇文章講的很細(xì),但是對(duì)于初學(xué)者也很難理解,在后面的筆記中,我會(huì)陳述自己的簡(jiǎn)單理解。?
?
摘要
??? 本文介紹 Java 平臺(tái)支持增補(bǔ)字符的方式。增補(bǔ)字符是 Unicode 標(biāo)準(zhǔn)中代碼點(diǎn)超出 U+FFFF 的字符,因此它們無(wú)法在 Java 編程語(yǔ)言中描述為單個(gè)的 16 位實(shí)體(例如char數(shù)據(jù)類型)。這些字符一般極少用,但是,有些會(huì)在諸如中文或日文人名中用到,因此,在東亞國(guó)家,政府應(yīng)用程序通常會(huì)要求支持這些字符。
??? Java 平臺(tái)目前正在改進(jìn),以便支持對(duì)增補(bǔ)字符的處理,這種改進(jìn)對(duì)現(xiàn)有的應(yīng)用程序影響微乎其微。新的低層 API 在需要時(shí)能夠使用單個(gè)的字符運(yùn)行。不過(guò),大多數(shù)文本處理 API 均使用字符序列,例如String類或字符數(shù)組。現(xiàn)在,這些均解釋為 UTF-16 序列,而且,這些 API 實(shí)現(xiàn)已轉(zhuǎn)變?yōu)檎_地處理增補(bǔ)字符。這些改進(jìn)已融入 Java 2 平臺(tái) 5.0 版,標(biāo)準(zhǔn)版 (J2SE)。
??? 除詳細(xì)解釋這些改進(jìn)之外,本文同時(shí)為應(yīng)用程序開發(fā)人員確定和實(shí)現(xiàn)必要的更改提供指導(dǎo),以支持整個(gè) Unicode 字符集的使用。
??? 背景
??? Unicode 最初設(shè)計(jì)是作為一種固定寬度的 16 位字符編碼。在 Java 編程語(yǔ)言中,基本數(shù)據(jù)類型char初衷是通過(guò)提供一種簡(jiǎn)單的、能夠包含任何字符的數(shù)據(jù)類型來(lái)充分利用這種設(shè)計(jì)的優(yōu)點(diǎn)。不過(guò),現(xiàn)在看來(lái),16 位編碼的所有 65,536 個(gè)字符并不能完全表示全世界所有正在使用或曾經(jīng)使用的字符。于是,Unicode 標(biāo)準(zhǔn)已擴(kuò)展到包含多達(dá) 1,112,064 個(gè)字符。那些超出原來(lái)的 16 位限制的字符被稱作增補(bǔ)字符。Unicode 標(biāo)準(zhǔn) 2.0 版是第一個(gè)包含啟用增補(bǔ)字符設(shè)計(jì)的版本,但是,直到 3.1 版才收入第一批增補(bǔ)字符集。由于 J2SE 的 5.0 版必須支持 Unicode 標(biāo)準(zhǔn) 4.0 版,因此它必須支持增補(bǔ)字符。
??? 對(duì)增補(bǔ)字符的支持也可能會(huì)成為東亞市場(chǎng)的一個(gè)普遍商業(yè)要求。政府應(yīng)用程序會(huì)需要這些增補(bǔ)字符,以正確表示一些包含罕見(jiàn)中文字符的姓名。出版應(yīng)用程序可能會(huì) 需要這些增補(bǔ)字符,以表示所有的古代字符和變體字符。中國(guó)政府要求支持 GB18030(一種對(duì)整個(gè) Unicode 字符集進(jìn)行編碼的字符編碼標(biāo)準(zhǔn)),因此,如果是 Unicode 3.1 版或更新版本,則將包括增補(bǔ)字符。臺(tái)灣標(biāo)準(zhǔn) CNS-11643 包含的許多字符在 Unicode 3.1 中列為增補(bǔ)字符。香港政府定義了一種針對(duì)粵語(yǔ)的字符集,其中的一些字符是 Unicode 中的增補(bǔ)字符。最后,日本的一些供應(yīng)商正計(jì)劃利用增補(bǔ)字符空間中大量的專用空間收入 50,000 多個(gè)日文漢字字符變體,以便從其專有系統(tǒng)遷移至基于 Java 平臺(tái)的解決方案。
??? 因此,Java 平臺(tái)不僅需要支持增補(bǔ)字符,而且必須使應(yīng)用程序能夠方便地做到這一點(diǎn)。由于增補(bǔ)字符打破了 Java 編程語(yǔ)言的基礎(chǔ)設(shè)計(jì)構(gòu)想,而且可能要求對(duì)編程模型進(jìn)行根本性的修改,因此,Java Community Process 召集了一個(gè)專家組,以期找到一個(gè)適當(dāng)?shù)慕鉀Q方案。該小組被稱為 JSR-204 專家組,使用Unicode 增補(bǔ)字符支持的 Java 技術(shù)規(guī)范請(qǐng)求的編號(hào)。從技術(shù)上來(lái)說(shuō),該專家組的決定僅適用于 J2SE 平臺(tái),但是由于 Java 2 平臺(tái)企業(yè)版 (J2EE) 處于 J2SE 平臺(tái)的最上層,因此它可以直接受益,我們期望 Java 2 平臺(tái)袖珍版 (J2ME) 的配置也采用相同的設(shè)計(jì)方法。
??? 不過(guò),在了解 JSR-204 專家組確定的解決方案之前,我們需要先理解一些術(shù)語(yǔ)。
??? 代碼點(diǎn)、字符編碼方案、UTF-16:這些是指什么?
??? 不幸的是,引入增補(bǔ)字符使字符模型變得更加復(fù)雜了。在過(guò)去,我們可以簡(jiǎn)單地說(shuō)“字符”,在一個(gè)基于 Unicode 的環(huán)境(例如 Java 平臺(tái))中,假定字符有 16 位,而現(xiàn)在我們需要更多的術(shù)語(yǔ)。我們會(huì)盡量介紹得相對(duì)簡(jiǎn)單一些 — 如需了解所有詳細(xì)的討論信息,您可以閱讀Unicode 標(biāo)準(zhǔn)第 2 章或 Unicode 技術(shù)報(bào)告 17“字符編碼模型”。Unicode 專業(yè)人士可略過(guò)所有介紹直接參閱本部分中的最后定義。
??? 字符是抽象的最小文本單位。它沒(méi)有固定的形狀(可能是一個(gè)字形),而且沒(méi)有值。“A”是一個(gè)字符,“€”(德國(guó)、法國(guó)和許多其他歐洲國(guó)家通用貨幣的標(biāo)志)也是一個(gè)字符。
??? 字符集是字符的集合。例如,漢字字符是中國(guó)人最先發(fā)明的字符,在中文、日文、韓文和越南文的書寫中使用。
??? 編碼字符集是一個(gè)字符集,它為每一個(gè)字符分配一個(gè)唯一數(shù)字。Unicode 標(biāo)準(zhǔn)的核心是一個(gè)編碼字符集,字母“A”的編碼為 004116 和字符“€”的編碼為20AC16.Unicode 標(biāo)準(zhǔn)始終使用十六進(jìn)制數(shù)字,而且在書寫時(shí)在前面加上前綴“U+”,所以“A”的編碼書寫為“U+0041”。
??? 代碼點(diǎn)是指可用于編碼字符集的數(shù)字。編碼字符集定義一個(gè)有效的代碼點(diǎn)范圍,但是并不一定將字符分配給所有這些代碼點(diǎn)。有效的 Unicode 代碼點(diǎn)范圍是 U+0000 至 U+10FFFF.Unicode 4.0 將字符分配給一百多萬(wàn)個(gè)代碼點(diǎn)中的 96,382 代碼點(diǎn)。
??? 增補(bǔ)字符是代碼點(diǎn)在 U+10000 至 U+10FFFF 范圍之間的字符,也就是那些使用原始的 Unicode 的 16 位設(shè)計(jì)無(wú)法表示的字符。從 U+0000 至 U+FFFF 之間的字符集有時(shí)候被稱為基本多語(yǔ)言面 (BMP)。因此,每一個(gè) Unicode 字符要么屬于 BMP,要么屬于增補(bǔ)字符。
??? 字符編碼方案是從一個(gè)或多個(gè)編碼字符集到一個(gè)或多個(gè)固定寬度代碼單元序列的映射。最常用的代碼單元是字節(jié),但是 16 位或 32 位整數(shù)也可用于內(nèi)部處理。UTF-32、UTF-16 和 UTF-8 是 Unicode 標(biāo)準(zhǔn)的編碼字符集的字符編碼方案。
??? UTF-32 即將每一個(gè) Unicode 代碼點(diǎn)表示為相同值的 32 位整數(shù)。很明顯,它是內(nèi)部處理最方便的表達(dá)方式,但是,如果作為一般字符串表達(dá)方式,則要消耗更多的內(nèi)存。
??? UTF-16 使用一個(gè)或兩個(gè)未分配的 16 位代碼單元的序列對(duì) Unicode 代碼點(diǎn)進(jìn)行編碼。值 U+0000 至 U+FFFF 編碼為一個(gè)相同值的 16 位單元。增補(bǔ)字符編碼為兩個(gè)代碼單元,第一個(gè)單元來(lái)自于高代理范圍(U+D800 至 U+DBFF),第二個(gè)單元來(lái)自于低代理范圍(U+DC00 至 U+DFFF)。這在概念上可能看起來(lái)類似于多字節(jié)編碼,但是其中有一個(gè)重要區(qū)別:值 U+D800 至 U+DFFF 保留用于 UTF-16;沒(méi)有這些值分配字符作為代碼點(diǎn)。這意味著,對(duì)于一個(gè)字符串中的每個(gè)單獨(dú)的代碼單元,軟件可以識(shí)別是否該代碼單元表示某個(gè)單單元字符,或者是 否該代碼單元是某個(gè)雙單元字符的第一個(gè)或第二單元。這相當(dāng)于某些傳統(tǒng)的多字節(jié)字符編碼來(lái)說(shuō)是一個(gè)顯著的改進(jìn),在傳統(tǒng)的多字節(jié)字符編碼中,字節(jié)值 0x41 既可能表示字母“A”,也可能是一個(gè)雙字節(jié)字符的第二個(gè)字節(jié)。
??? UTF-8 使用一至四個(gè)字節(jié)的序列對(duì)編碼 Unicode 代碼點(diǎn)進(jìn)行編碼。U+0000 至 U+007F 使用一個(gè)字節(jié)編碼,U+0080 至 U+07FF 使用兩個(gè)字節(jié),U+0800 至 U+FFFF 使用三個(gè)字節(jié),而 U+10000 至 U+10FFFF 使用四個(gè)字節(jié)。UTF-8 設(shè)計(jì)原理為:字節(jié)值 0x00 至 0x7F 始終表示代碼點(diǎn) U+0000 至 U+007F(Basic Latin 字符子集,它對(duì)應(yīng) ASCII 字符集)。這些字節(jié)值永遠(yuǎn)不會(huì)表示其他代碼點(diǎn),這一特性使 UTF-8 可以很方便地在軟件中將特殊的含義賦予某些 ASCII 字符。
??? 下表所示為幾個(gè)字符不同表達(dá)方式的比較:
?
| Unicode 代碼點(diǎn) | U+0041 | U+00DF | U+6771 | U+10400 | ||||||||||
| 表示字形 | ||||||||||||||
| UTF-32 代碼單元 |
|
|
|
| ||||||||||
| UTF-16 代碼單元 |
|
|
|
| ||||||||||
| UTF-8 代碼單元 |
|
|
|
|
?
?
??? 另外,本文在許多地方使用術(shù)語(yǔ)字符序列或char序列概括 Java 2 平臺(tái)識(shí)別的所有字符序列的容器:char[], java.lang.CharSequence的實(shí)現(xiàn)(例如String類),和java.text.CharacterIterator的實(shí)現(xiàn)。
??? 這么多術(shù)語(yǔ)。它們與在 Java 平臺(tái)中支持增補(bǔ)字符有什么關(guān)系呢?
?
?? Java 平臺(tái)中增補(bǔ)字符的設(shè)計(jì)方法
??? JSR-204 專家組必須作出的主要決定是如何在 Java API 中表示增補(bǔ)字符,包括單個(gè)字符和所有形式的字符序列。專家組考慮并排除了多種方法:
??????? 重新定義基本類型char,使其具有 32 位,這樣也會(huì)使所有形式的char序列成為 UTF-32 序列。
??????? 在現(xiàn)有的 16 位類型char的基礎(chǔ)上,為字符引入一種新的 32 位基本類型(例如,char32)。所有形式的 Char 序列均基于 UTF-16.
??????? 在現(xiàn)有的 16 位類型char的基礎(chǔ)上,為字符引入一種新的 32 位基本類型(例如,char32)。String和StringBuffer接受并行 API,并將它們解釋為 UTF-16 序列或 UTF-32 序列;其他char序列繼續(xù)基于 UTF-16.
??????? 使用int表示增補(bǔ)的代碼點(diǎn)。String和StringBuffer接受并行 API,并將它們解釋為 UTF-16 序列或 UTF-32 序列;其他char序列繼續(xù)基于 UTF-16.
??????? 使用代理char對(duì),表示增補(bǔ)代碼點(diǎn)。所有形式的char序列基于 UTF-16.
??????? 引入一種封裝字符的類。String和StringBuffer接受新的 API,并將它們解釋為此類字符的序列。
??????? 使用一個(gè)CharSequence實(shí)例和一個(gè)索引的組合表示代碼點(diǎn)。
??? 在這些方法中,一些在早期就被排除了。例如,重新定義基本類型char,使其具有 32 位,這對(duì)于全新的平臺(tái)可能會(huì)非常有吸引力,但是,對(duì)于 J2SE 來(lái)說(shuō),它會(huì)與現(xiàn)有的 Java 虛擬機(jī)1、序列化和其他接口不兼容,更不用說(shuō)基于 UTF-32 的字符串要使用兩倍于基于 UTF-16 的字符串的內(nèi)存了。添加一種新類型的char32可能會(huì)簡(jiǎn)單一些,但是仍然會(huì)出現(xiàn)虛擬機(jī)和序列化方面的問(wèn)題。而且,語(yǔ)言更改通常需要比 API 更改有更長(zhǎng)的提前期,因此,前面兩種方法會(huì)對(duì)增補(bǔ)字符支持帶來(lái)無(wú)法接受的延遲。為了在余下的方法中篩選出最優(yōu)方案,實(shí)現(xiàn)小組使用四種不同的方法,在大量進(jìn) 行低層字符處理的代碼(java.util.regex包)中實(shí)現(xiàn)了對(duì)增補(bǔ)字符支持,并對(duì)這四種方法的難易程度和運(yùn)行表現(xiàn)進(jìn)行了比較。
??? 最終,專家組確定了一種分層的方法:
??????? 使用基本類型int在低層 API 中表示代碼點(diǎn),例如Character類的靜態(tài)方法。
??????? 將所有形式的char序列均解釋為 UTF-16 序列,并促進(jìn)其在更高層級(jí) API 中的使用。
??????? 提供 API,以方便在各種char和基于代碼點(diǎn)的表示法之間的轉(zhuǎn)換。
??? 在需要時(shí),此方法既能夠提供一種概念簡(jiǎn)明且高效的單個(gè)字符表示法,又能夠充分利用通過(guò)改進(jìn)可支持增補(bǔ)字符的現(xiàn)有 API.同時(shí),還能夠促進(jìn)字符序列在單個(gè)字符上的應(yīng)用,這一點(diǎn)一般對(duì)于國(guó)際化的軟件很有好處。
??? 在這種方法中,一個(gè)char表示一個(gè) UTF-16 代碼單元,這樣對(duì)于表示代碼點(diǎn)有時(shí)并不夠用。您會(huì)注意到,J2SE 技術(shù)規(guī)范現(xiàn)在使用術(shù)語(yǔ)代碼點(diǎn)和 UTF-16 代碼單元(表示法是相關(guān)的)以及通用術(shù)語(yǔ)字符(表示法與該討論沒(méi)有關(guān)系)。API 通常使用名稱codePoint描述表示代碼點(diǎn)的類型int的變量,而 UTF-16 代碼單元的類型當(dāng)然為char.我們將在下面兩部分中了解到 J2SE 平臺(tái)的實(shí)質(zhì)變化 — 其中一部分介紹單個(gè)代碼點(diǎn)的低層 API,另一部分介紹采用字符序列的高層接口。
??? 開放的增補(bǔ)字符:基于代碼點(diǎn)的 API
??? 新增的低層 API 分為兩大類:用于各種char和基于代碼點(diǎn)的表示法之間轉(zhuǎn)換的方法和用于分析和映射代碼點(diǎn)的方法。
??? 最基本的轉(zhuǎn)換方法是Character.toCodePoint(char high, char low)(用于將兩個(gè) UTF-16 代碼單元轉(zhuǎn)換為一個(gè)代碼點(diǎn))和Character.toChars(int codePoint)(用于將指定的代碼點(diǎn)轉(zhuǎn)換為一個(gè)或兩個(gè) UTF-16 代碼單元,然后封裝到一個(gè)char[]內(nèi)。不過(guò),由于大多數(shù)情況下文本以字符序列的形式出現(xiàn),因此,另外提供codePointAt和 codePointBefore方法,用于將代碼點(diǎn)從各種字符序列表示法中提取出來(lái):Character.codePointAt(char[] a, int index)和String.codePointBefore(int index)是兩種典型的例子。在將代碼點(diǎn)插入字符序列時(shí),大多數(shù)情況下均有一些針對(duì)StringBuffer和StringBuilder類的 appendCodePoint(int codePoint)方法,以及一個(gè)用于提取表示代碼點(diǎn)的int[]的String構(gòu)建器。
??? 幾種用于分析代碼單元和代碼點(diǎn)的方法有助于轉(zhuǎn)換過(guò)程:Character 類中的isHighSurrogate和isLowSurrogate方法可以識(shí)別用于表示增補(bǔ)字符的char值;charCount(int codePoint)方法可以確定是否需要將某個(gè)代碼點(diǎn)轉(zhuǎn)換為一個(gè)或兩個(gè)char.但是,大多數(shù)基于代碼點(diǎn)的方法均能夠?qū)λ?Unicode 字符實(shí)現(xiàn)基于char的舊方法對(duì) BMP 字符所實(shí)現(xiàn)的功能。以下是一些典型例子:
??? Character.isLetter(int codePoint)可根據(jù) Unicode 標(biāo)準(zhǔn)識(shí)別字母。
??? Character.isJavaIdentifierStart(int codePoint)可根據(jù) Java 語(yǔ)言規(guī)范確定代碼點(diǎn)是否可以啟動(dòng)標(biāo)識(shí)符。
??? Character.UnicodeBlock.of(int codePoint)可搜索代碼點(diǎn)所屬的 Unicode 字符子集。
??? Character.toUpperCase(int codePoint)可將給定的代碼點(diǎn)轉(zhuǎn)換為其大寫等值字符。盡管此方法能夠支持增補(bǔ)字符,但是它仍然不能解決根本的問(wèn)題,即在某些情況下,逐個(gè)字符的轉(zhuǎn) 換無(wú)法正確完成。例如,德文字符“"?"”應(yīng)該轉(zhuǎn)換為“SS”,這需要使用String.toUpperCase方法。
??? 注意大多數(shù)接受代碼點(diǎn)的方法并不檢查給定的int值是否處于有效的 Unicode 代碼點(diǎn)范圍之內(nèi)(如上所述,只有 0x0 至 0x10FFFF 之間的范圍是有效的)。在大多數(shù)情況下,該值是以確保其有效的方法產(chǎn)生的,在這些低層 API 中反復(fù)檢查其有效性可能會(huì)對(duì)系統(tǒng)性能造成負(fù)面的影響。在無(wú)法確保有效性的情況下,應(yīng)用程序必須使用Character.isValidCodePoint 方法確保代碼點(diǎn)有效。大多數(shù)方法對(duì)于無(wú)效的代碼點(diǎn)采取的行為沒(méi)有特別加以指定,不同的實(shí)現(xiàn)可能會(huì)有所不同。
??? API 包含許多簡(jiǎn)便的方法,這些方法可使用其他低層的 API 實(shí)現(xiàn),但是專家組覺(jué)得,這些方法很常用,將它們添加到 J2SE 平臺(tái)上很有意義。不過(guò),專家組也排除了一些建議的簡(jiǎn)便方法,這給我們提供了一次展示自己實(shí)現(xiàn)此類方法能力的機(jī)會(huì)。例如,專家組經(jīng)過(guò)討論,排除了一種針對(duì) String類的新構(gòu)建器(該構(gòu)建器可以創(chuàng)建一個(gè)保持單個(gè)代碼點(diǎn)的String)。以下是使應(yīng)用程序使用現(xiàn)有的 API 提供功能的一種簡(jiǎn)便方法:
?
| /** * 創(chuàng)建僅含有指定代碼點(diǎn)的新 String. */ String newString(int codePoint) {? ??? return new String(Character.toChars(codePoint)); }? |
?
??? 您會(huì)注意到,在這個(gè)簡(jiǎn)單的實(shí)現(xiàn)中,toChars方法始終創(chuàng)建一個(gè)中間數(shù)列,該數(shù)列僅使用一次即立即丟棄。如果該方法在您的性能評(píng)估中出現(xiàn),您可能會(huì)希望將其優(yōu)化為針對(duì)最為普通的情況,即該代碼點(diǎn)為 BMP 字符:
?
| /** |
?
??? 或者,如果您需要?jiǎng)?chuàng)建許多個(gè)這樣的 string,則可能希望編寫一個(gè)重復(fù)使用toChars方法所使用的數(shù)列的通用版本:
?
| /** |
?
??? 不過(guò),最終您可能會(huì)發(fā)現(xiàn),您需要的是一個(gè)完全不同的解決方案。新的構(gòu)建器String(int codePoint)實(shí)際上建議作為String.valueOf(char)的一個(gè)基于代碼點(diǎn)的備選方案。在很多情況下,此方法用于消息生成的環(huán)境,例如:
?
| System.out.println("Character " + String.valueOf(char) + " is invalid.");? |
?
??? 新的格式化 API支持增補(bǔ)文字,提供一種更加簡(jiǎn)單的備選方案:
?
| System.out.printf("Character %c is invalid.%n", codePoint);? |
?
??? 使用此高層 API 不僅簡(jiǎn)捷,而它有很多特殊的優(yōu)點(diǎn):它可以避免串聯(lián)(串聯(lián)會(huì)使消息很難本地化),并將需要移進(jìn)資源包 (resource bundle) 的字符串?dāng)?shù)量從兩個(gè)減少到一個(gè)。
?
增補(bǔ)字符透視:功能增強(qiáng)
??? 在支持使用增補(bǔ)字符的 Java 2 平臺(tái)中的大部分更改沒(méi)有反映到新的 API 內(nèi)。一般預(yù)期是,處理字符序列的所有接口將以適合其功能的方式處理增補(bǔ)字符。本部分著重講述為達(dá)到此預(yù)期所作一些功能增強(qiáng)。
??? Java 編程語(yǔ)言中的標(biāo)識(shí)符
??? Java 語(yǔ)言規(guī)范指出所有 Unicode 字母和數(shù)字均可用于標(biāo)識(shí)符。許多增補(bǔ)字符是字母或數(shù)字,因此 Java 語(yǔ)言規(guī)范已經(jīng)參照新的基于代碼點(diǎn)的方法進(jìn)行更新,以在標(biāo)識(shí)符內(nèi)定義合法字符。為使用這些新方法,需要檢測(cè)標(biāo)識(shí)符的 javac 編譯器和其他工具都進(jìn)行了修訂。
??? 庫(kù)內(nèi)的增補(bǔ)字符支持
??? 許多 J2SE 庫(kù)已經(jīng)過(guò)增強(qiáng),可以通過(guò)現(xiàn)有接口支持增補(bǔ)字符。以下是一些例子:
??????? 字符串大小寫轉(zhuǎn)換功能已更新,可以處理增補(bǔ)字符,也可以實(shí)現(xiàn) Unicode 標(biāo)準(zhǔn)中規(guī)定的特殊大小寫規(guī)則。
??????? java.util.regex包已更新,這樣模式字符串和目標(biāo)字符串均可以包含增補(bǔ)字符并將其作為完整單元處理。
??????? 現(xiàn)在,在java.text包內(nèi)進(jìn)行整理處理時(shí),會(huì)將增補(bǔ)字符看作完整單元。
??????? java.text.Bidi類已更新,可以處理增補(bǔ)字符和 Unicode 4.0 中新增的其他字符。請(qǐng)注意,Cypriot Syllabary 字符子集內(nèi)的增補(bǔ)字符具有從右至左的方向性。
??????? Java 2D API 內(nèi)的字體渲染和打印技術(shù)已經(jīng)過(guò)增強(qiáng),可以正確渲染和測(cè)量包含增補(bǔ)字符的字符串。
??????? Swing 文本組件實(shí)現(xiàn)已更新,可以處理包含增補(bǔ)字符的文本。
??? 字符轉(zhuǎn)換
??? 只有很少的字符編碼可以表示增補(bǔ)字符。如果是基于 Unicode 的編碼(如 UTF-8 和 UTF-16LE),則舊版的 J2RE 內(nèi)的字符轉(zhuǎn)換器已經(jīng)按照正確處理增補(bǔ)字符的方式實(shí)現(xiàn)轉(zhuǎn)換。對(duì)于 J2RE 5.0,可以表示增補(bǔ)字符的其他編碼的轉(zhuǎn)換器已更新:GB18030、x-EUC-TW(現(xiàn)在實(shí)現(xiàn)所有 CNS 11643 層面)和 Big5-HKSCS(現(xiàn)在實(shí)現(xiàn) HKSCS-2001)。
??? 在源文件內(nèi)表示增補(bǔ)字符
??? 在 Java 編程語(yǔ)言源文件中,如果使用可以直接表示增補(bǔ)字符的字符編碼,則使用增補(bǔ)字符最為方便。UTF-8 是最佳的選擇。在所使用的字符編碼無(wú)法直接表示字符的情況下,Java 編程語(yǔ)言提供一種 Unicode 轉(zhuǎn)義符語(yǔ)法。此語(yǔ)法沒(méi)有經(jīng)過(guò)增強(qiáng),無(wú)法直接表示增補(bǔ)字符。而是使用兩個(gè)連續(xù)的 Unicode 轉(zhuǎn)義符將其表示為 UTF-16 字符表示法中的兩個(gè)編碼單元。例如,字符 U+20000 寫作“/uD840/uDC00”。您也許不愿意探究這些轉(zhuǎn)義序列的含義;最好是寫入支持所需增補(bǔ)字符的編碼,然后使用一種工具(如 native2ascii)將其轉(zhuǎn)換為轉(zhuǎn)義序列。
??? 遺憾的是,由于其編碼問(wèn)題,屬性文件仍局限于 ISO 8859-1(除非您的應(yīng)用程序使用新的 XML 格式)。這意味著您始終必須對(duì)增補(bǔ)字符使用轉(zhuǎn)義序列,而且可能要使用不同的編碼進(jìn)行編寫,然后使用諸如 native2ascii 的工具進(jìn)行轉(zhuǎn)換。
??? 經(jīng)修訂的 UTF-8
??? Java 平臺(tái)對(duì)經(jīng)修訂的 UTF-8 已經(jīng)很熟悉,但是,問(wèn)題是應(yīng)用程序開發(fā)人員在可能包含增補(bǔ)字符的文本和 UTF-8 之間進(jìn)行轉(zhuǎn)換時(shí)需要更加留神。需要特別注意的是,某些 J2SE 接口使用的編碼與 UTF-8 相似但與其并不兼容。以前,此編碼有時(shí)被稱為“Java modified UTF-8”(經(jīng) Java 修訂的 UTF-8)或(錯(cuò)誤地)直接稱為“UTF-8”。對(duì)于 J2SE 5.0,其說(shuō)明文檔正在更新,此編碼將統(tǒng)稱為“modified UTF-8”(經(jīng)修訂的 UTF-8)。
??? 經(jīng)修訂的 UTF-8 和標(biāo)準(zhǔn) UTF-8 之間之所以不兼容,其原因有兩點(diǎn)。其一,經(jīng)修訂的 UTF-8 將字符 U+0000 表示為雙字節(jié)序列 0xC0 0x80,而標(biāo)準(zhǔn) UTF-8 使用單字節(jié)值 0x0.其二,經(jīng)修訂的 UTF-8 通過(guò)對(duì)其 UTF-16 表示法的兩個(gè)代理代碼單元單獨(dú)進(jìn)行編碼表示增補(bǔ)字符。每個(gè)代理代碼單元由三個(gè)字節(jié)來(lái)表示,共有六個(gè)字節(jié)。而標(biāo)準(zhǔn) UTF-8 使用單個(gè)四字節(jié)序列表示整個(gè)字符。
??? Java 虛擬機(jī)及其附帶的接口(如 Java 本機(jī)接口、多種工具接口或 Java 類文件)在java.io.DataInput和DataOutput接口和類中使用經(jīng)修訂的 UTF-8 實(shí)現(xiàn)或使用這些接口和類,并進(jìn)行序列化。Java 本機(jī)接口提供與經(jīng)修訂的 UTF-8 之間進(jìn)行轉(zhuǎn)換的例程。而標(biāo)準(zhǔn) UTF-8 由String類、java.io.InputStreamReader和OutputStreamWriter類、java.nio.charset設(shè) 施 (facility) 以及許多其上層的 API 提供支持。
??? 由于經(jīng)修訂的 UTF-8 與標(biāo)準(zhǔn)的 UTF-8 不兼容,因此切勿同時(shí)使用這兩種版本的編碼。經(jīng)修訂的 UTF-8 只能與上述的 Java 接口配合使用。在任何其他情況下,尤其對(duì)于可能來(lái)自非基于 Java 平臺(tái)的軟件的或可能通過(guò)其編譯的數(shù)據(jù)流,必須使用標(biāo)準(zhǔn)的 UTF-8.需要使用標(biāo)準(zhǔn)的 UTF-8 時(shí),則不能使用 Java 本機(jī)接口例程與經(jīng)修訂的 UTF-8 進(jìn)行轉(zhuǎn)換。
??? 在應(yīng)用程序內(nèi)支持增補(bǔ)字符
??? 現(xiàn)在,對(duì)大多數(shù)讀者來(lái)說(shuō)最為重要的問(wèn)題是:必須對(duì)應(yīng)用程序進(jìn)行哪些更改才能支持增補(bǔ)字符?
??? 答案取決于在應(yīng)用程序中進(jìn)行哪種類型的文本處理和使用哪些 Java 平臺(tái) API.對(duì)于僅以各種形式char序列([char[]、java.lang.CharSequence實(shí)現(xiàn)、 java.text.CharacterIterator實(shí)現(xiàn))處理文本和僅使用接受和退回序列(如char序列)的 Java API 的應(yīng)用程序,可能根本不需要進(jìn)行任何更改。Java 平臺(tái) API 的實(shí)現(xiàn)應(yīng)該能夠處理增補(bǔ)字符。
??? 對(duì)于本身解釋單個(gè)字符、將單個(gè)字符傳送給 Java 平臺(tái) API 或調(diào)用能夠返回單個(gè)字符的方法的應(yīng)用程序,則需要考慮這些字符的有效值。在很多情況下,往往不要求支持增補(bǔ)字符。例如,如果某應(yīng)用程序搜索char序列中 的 HTML 標(biāo)記,并逐一檢查每個(gè)char,它會(huì)知道這些標(biāo)記僅使用 Basic Latin 字符子集中的字符。如果所搜索的文本含有增補(bǔ)字符,則這些字符不會(huì)與標(biāo)記字符混淆,因?yàn)?UTF-16 使用代碼單元表示增補(bǔ)字符,而代碼單元的值不會(huì)用于 BMP 字符。
??? 只有在某應(yīng)用程序本身解釋單個(gè)字符、將單個(gè)字符傳送給 Java 平臺(tái) API 或調(diào)用能夠返回單個(gè)字符的方法且這些字符可能為增補(bǔ)字符時(shí),才必須更改該應(yīng)用程序。在提供使用char序列的并行 API 時(shí),最好轉(zhuǎn)而使用此類 API.在其他情況下,有必要使用新的 API 在char和基于代碼點(diǎn)的表示法之間進(jìn)行轉(zhuǎn)換,并調(diào)用基于代碼點(diǎn)的 API.當(dāng)然,如果您發(fā)現(xiàn)在 J2SE 5.0 中有更新、更方便的 API,使您能夠支持增補(bǔ)字符并同時(shí)簡(jiǎn)化代碼(如上格式化范例中所述),則沒(méi)有必要這樣做。
??? 您可能會(huì)猶豫,是將所有文本轉(zhuǎn)換為代碼點(diǎn)表示法(即int[])然后在該表示法中處理,還是在大多數(shù)情況下仍采用char序列,僅在需要時(shí)轉(zhuǎn)換為代碼點(diǎn), 兩者之間孰優(yōu)孰劣很難確定。當(dāng)然,總體來(lái)說(shuō),Java 平臺(tái) API 相對(duì)于char序列肯定具有一定的優(yōu)勢(shì),而且采用 Java 平臺(tái) API 可以節(jié)省內(nèi)存空間。
??? 對(duì)于需要與 UTF-8 之間進(jìn)行轉(zhuǎn)換的應(yīng)用程序,還需要認(rèn)真考慮是需要標(biāo)準(zhǔn)的 UTF-8 還是經(jīng)修訂的 UTF-8,并針對(duì)每種 UTF-8 采用適當(dāng)?shù)?Java 平臺(tái)。“經(jīng)修訂的 UTF-8”部分介紹進(jìn)行正確選擇所需的信息。
??? 使用增補(bǔ)字符測(cè)試應(yīng)用程序
??? 經(jīng)過(guò)前面部分的介紹后,無(wú)論您是否需要修訂應(yīng)用程序,測(cè)試應(yīng)用程序是否運(yùn)行正常始終是一種正確的做法。對(duì)于不含有圖形用戶界面的應(yīng)用程序,有關(guān)“在源文件內(nèi)表示增補(bǔ)字符” 的信息有助于設(shè)計(jì)測(cè)試用例。以下是有關(guān)使用圖形用戶界面進(jìn)行測(cè)試的補(bǔ)充信息。
??? 對(duì)于文本輸入,Java 2 SDK提供用于接受“/Uxxxxxx”格式字符串的代碼點(diǎn)輸入方法,這里大寫的“U”表示轉(zhuǎn)義序列包含六個(gè)十六進(jìn)制數(shù)字,因此允許使用增補(bǔ)字符。小寫的 “u”表示轉(zhuǎn)義序列“/uxxxx”的原始格式。您可以在 J2SDK 目錄 demo/jfc/CodePointIM 內(nèi)找到此輸入方法及其說(shuō)明文檔。
??? 對(duì)于字體渲染,您需要至少能夠渲染一些增補(bǔ)字符的字體。其中一種此類字體為 James Kass 的Code2001字體,它提供手寫體字形(如 Deseret 和 Old Italic)。利用 Java 2D 庫(kù)中提供新功能,您只需將該字體安裝到 J2RE 的 lib/fonts/fallback 目錄內(nèi)即可,然后它可自動(dòng)添加至在 2D 和 XAWT 渲染時(shí)使用的所有邏輯字體 — 無(wú)需編輯字體配置文件。
??? 至此,您就可以確認(rèn),您的應(yīng)用程序能夠完全支持增補(bǔ)字符了!
??? 結(jié)論
??? 對(duì)增補(bǔ)字符的支持已經(jīng)引入 Java 平臺(tái),大部分應(yīng)用程序無(wú)需更改代碼即可處理這些字符。解釋單個(gè)字符的應(yīng)用程序可以在Character類和多種CharSequence子類中使用基于代碼點(diǎn)的新 API.
??? 以下是Unicode和UTF-8之間的轉(zhuǎn)換關(guān)系表:
?
| 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ù)組轉(zhuǎn)整數(shù):
?
| static int bytes2int(byte[] b) 整數(shù)轉(zhuǎn)byte數(shù)組: static byte[] int2bytes(int num) |
?
?
轉(zhuǎn)載于:https://my.oschina.net/lhplj/blog/735933
總結(jié)
以上是生活随笔為你收集整理的Java中的代码点和代码单元的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。