鷹翔宇空

          學(xué)習(xí)和生活

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            110 Posts :: 141 Stories :: 315 Comments :: 1 Trackbacks
          原文引自:http://java.sun.com/developer/technicalArticles/Intl/Supplementary/index_zh_CN.html

          2004 年 5 月

          English: Supplementary Characters in the Java Platform
          日本語: Java プラットフォームにおける補(bǔ)助文字のサポート

          摘要

          本文介紹 Java 平臺支持增補(bǔ)字符的方式。增補(bǔ)字符是 Unicode 標(biāo)準(zhǔn)中代碼點(diǎn)超出 U+FFFF 的字符,因此它們無法在 Java 編程語言中描述為單個的 16 位實(shí)體(例如 char 數(shù)據(jù)類型)。這些字符一般極少用,但是,有些會在諸如中文或日文人名中用到,因此,在東亞國家,政府應(yīng)用程序通常會要求支持這些字符。

          Java 平臺目前正在改進(jìn),以便支持對增補(bǔ)字符的處理,這種改進(jìn)對現(xiàn)有的應(yīng)用程序影響微乎其微。新的低層 API 在需要時能夠使用單個的字符運(yùn)行。不過,大多數(shù)文本處理 API 均使用字符序列,例如 String 類或字符數(shù)組。現(xiàn)在,這些均解釋為 UTF-16 序列,而且,這些 API 實(shí)現(xiàn)已轉(zhuǎn)變?yōu)檎_地處理增補(bǔ)字符。這些改進(jìn)已融入 Java 2 平臺 5.0 版,標(biāo)準(zhǔn)版 (J2SE)。

          除詳細(xì)解釋這些改進(jìn)之外,本文同時為應(yīng)用程序開發(fā)人員確定和實(shí)現(xiàn)必要的更改提供指導(dǎo),以支持整個 Unicode 字符集的使用。

          背景

          Unicode 最初設(shè)計是作為一種固定寬度的 16 位字符編碼。在 Java 編程語言中,基本數(shù)據(jù)類型 char 初衷是通過提供一種簡單的、能夠包含任何字符的數(shù)據(jù)類型來充分利用這種設(shè)計的優(yōu)點(diǎn)。不過,現(xiàn)在看來,16 位編碼的所有 65,536 個字符并不能完全表示全世界所有正在使用或曾經(jīng)使用的字符。于是,Unicode 標(biāo)準(zhǔn)已擴(kuò)展到包含多達(dá) 1,112,064 個字符。那些超出原來的 16 位限制的字符被稱作增補(bǔ)字符。Unicode 標(biāo)準(zhǔn) 2.0 版是第一個包含啟用增補(bǔ)字符設(shè)計的版本,但是,直到 3.1 版才收入第一批增補(bǔ)字符集。由于 J2SE 的 5.0 版必須支持 Unicode 標(biāo)準(zhǔn) 4.0 版,因此它必須支持增補(bǔ)字符。

          對增補(bǔ)字符的支持也可能會成為東亞市場的一個普遍商業(yè)要求。政府應(yīng)用程序會需要這些增補(bǔ)字符,以正確表示一些包含罕見中文字符的姓名。出版應(yīng)用程序可能會需要這些增補(bǔ)字符,以表示所有的古代字符和變體字符。中國政府要求支持 GB18030(一種對整個 Unicode 字符集進(jìn)行編碼的字符編碼標(biāo)準(zhǔn)),因此,如果是 Unicode 3.1 版或更新版本,則將包括增補(bǔ)字符。臺灣標(biāo)準(zhǔn) CNS-11643 包含的許多字符在 Unicode 3.1 中列為增補(bǔ)字符。香港政府定義了一種針對粵語的字符集,其中的一些字符是 Unicode 中的增補(bǔ)字符。最后,日本的一些供應(yīng)商正計劃利用增補(bǔ)字符空間中大量的專用空間收入 50,000 多個日文漢字字符變體,以便從其專有系統(tǒng)遷移至基于 Java 平臺的解決方案。

          因此,Java 平臺不僅需要支持增補(bǔ)字符,而且必須使應(yīng)用程序能夠方便地做到這一點(diǎn)。由于增補(bǔ)字符打破了 Java 編程語言的基礎(chǔ)設(shè)計構(gòu)想,而且可能要求對編程模型進(jìn)行根本性的修改,因此,Java Community Process 召集了一個專家組,以期找到一個適當(dāng)?shù)慕鉀Q方案。該小組被稱為 JSR-204 專家組,使用 Unicode 增補(bǔ)字符支持的 Java 技術(shù)規(guī)范請求的編號。從技術(shù)上來說,該專家組的決定僅適用于 J2SE 平臺,但是由于 Java 2 平臺企業(yè)版 (J2EE) 處于 J2SE 平臺的最上層,因此它可以直接受益,我們期望 Java 2 平臺袖珍版 (J2ME) 的配置也采用相同的設(shè)計方法。

          不過,在了解 JSR-204 專家組確定的解決方案之前,我們需要先理解一些術(shù)語。

          代碼點(diǎn)、字符編碼方案、UTF-16:這些是指什么?

          不幸的是,引入增補(bǔ)字符使字符模型變得更加復(fù)雜了。在過去,我們可以簡單地說“字符”,在一個基于 Unicode 的環(huán)境(例如 Java 平臺)中,假定字符有 16 位,而現(xiàn)在我們需要更多的術(shù)語。我們會盡量介紹得相對簡單一些 — 如需了解所有詳細(xì)的討論信息,您可以閱讀 Unicode 標(biāo)準(zhǔn)第 2 章或 Unicode 技術(shù)報告 17“字符編碼模型”。Unicode 專業(yè)人士可略過所有介紹直接參閱本部分中的最后定義。

          字符是抽象的最小文本單位。它沒有固定的形狀(可能是一個字形),而且沒有值。“A”是一個字符,“€”(德國、法國和許多其他歐洲國家通用貨幣的標(biāo)志)也是一個字符。

          字符集是字符的集合。例如,漢字字符是中國人最先發(fā)明的字符,在中文、日文、韓文和越南文的書寫中使用。

          編碼字符集是一個字符集,它為每一個字符分配一個唯一數(shù)字。Unicode 標(biāo)準(zhǔn)的核心是一個編碼字符集,字母“A”的編碼為 004116 和字符“€”的編碼為 20AC16。Unicode 標(biāo)準(zhǔn)始終使用十六進(jìn)制數(shù)字,而且在書寫時在前面加上前綴“U+”,所以“A”的編碼書寫為“U+0041”。

          代碼點(diǎn)是指可用于編碼字符集的數(shù)字。編碼字符集定義一個有效的代碼點(diǎn)范圍,但是并不一定將字符分配給所有這些代碼點(diǎn)。有效的 Unicode 代碼點(diǎn)范圍是 U+0000 至 U+10FFFF。Unicode 4.0 將字符分配給一百多萬個代碼點(diǎn)中的 96,382 代碼點(diǎn)。

          增補(bǔ)字符是代碼點(diǎn)在 U+10000 至 U+10FFFF 范圍之間的字符,也就是那些使用原始的 Unicode 的 16 位設(shè)計無法表示的字符。從 U+0000 至 U+FFFF 之間的字符集有時候被稱為基本多語言面 (BMP)。因此,每一個 Unicode 字符要么屬于 BMP,要么屬于增補(bǔ)字符。

          字符編碼方案是從一個或多個編碼字符集到一個或多個固定寬度代碼單元序列的映射。最常用的代碼單元是字節(jié),但是 16 位或 32 位整數(shù)也可用于內(nèi)部處理。UTF-32、UTF-16 和 UTF-8 是 Unicode 標(biāo)準(zhǔn)的編碼字符集的字符編碼方案。

          UTF-32 即將每一個 Unicode 代碼點(diǎn)表示為相同值的 32 位整數(shù)。很明顯,它是內(nèi)部處理最方便的表達(dá)方式,但是,如果作為一般字符串表達(dá)方式,則要消耗更多的內(nèi)存。

          UTF-16 使用一個或兩個未分配的 16 位代碼單元的序列對 Unicode 代碼點(diǎn)進(jìn)行編碼。值 U+0000 至 U+FFFF 編碼為一個相同值的 16 位單元。增補(bǔ)字符編碼為兩個代碼單元,第一個單元來自于高代理范圍(U+D800 至 U+DBFF),第二個單元來自于低代理范圍(U+DC00 至 U+DFFF)。這在概念上可能看起來類似于多字節(jié)編碼,但是其中有一個重要區(qū)別:值 U+D800 至 U+DFFF 保留用于 UTF-16;沒有這些值分配字符作為代碼點(diǎn)。這意味著,對于一個字符串中的每個單獨(dú)的代碼單元,軟件可以識別是否該代碼單元表示某個單單元字符,或者是否該代碼單元是某個雙單元字符的第一個或第二單元。這相當(dāng)于某些傳統(tǒng)的多字節(jié)字符編碼來說是一個顯著的改進(jìn),在傳統(tǒng)的多字節(jié)字符編碼中,字節(jié)值 0x41 既可能表示字母“A”,也可能是一個雙字節(jié)字符的第二個字節(jié)。

          UTF-8 使用一至四個字節(jié)的序列對編碼 Unicode 代碼點(diǎn)進(jìn)行編碼。U+0000 至 U+007F 使用一個字節(jié)編碼,U+0080 至 U+07FF 使用兩個字節(jié),U+0800 至 U+FFFF 使用三個字節(jié),而 U+10000 至 U+10FFFF 使用四個字節(jié)。UTF-8 設(shè)計原理為:字節(jié)值 0x00 至 0x7F 始終表示代碼點(diǎn) U+0000 至 U+007F(Basic Latin 字符子集,它對應(yīng) ASCII 字符集)。這些字節(jié)值永遠(yuǎn)不會表示其他代碼點(diǎn),這一特性使 UTF-8 可以很方便地在軟件中將特殊的含義賦予某些 ASCII 字符。

          下表所示為幾個字符不同表達(dá)方式的比較:

          Unicode 代碼點(diǎn)
          U+0041
          U+00DF
          U+6771
          U+10400
          表示字形
          UTF-32 代碼單元
          00000041
          000000DF
          00006771
          00010400
          UTF-16 代碼單元
          0041
          00DF
          6771
          D801DC00
          UTF-8 代碼單元
          41
          C39F
          E69DB1
          F0909080

          另外,本文在許多地方使用術(shù)語字符序列或 char 序列概括 Java 2 平臺識別的所有字符序列的容器:char[], java.lang.CharSequence 的實(shí)現(xiàn)(例如 String 類),和 java.text.CharacterIterator 的實(shí)現(xiàn)。

          這么多術(shù)語。它們與在 Java 平臺中支持增補(bǔ)字符有什么關(guān)系呢?

          Java 平臺中增補(bǔ)字符的設(shè)計方法

          JSR-204 專家組必須作出的主要決定是如何在 Java API 中表示增補(bǔ)字符,包括單個字符和所有形式的字符序列。專家組考慮并排除了多種方法:

          • 重新定義基本類型 char,使其具有 32 位,這樣也會使所有形式的 char 序列成為 UTF-32 序列。
          • 在現(xiàn)有的 16 位類型 char 的基礎(chǔ)上,為字符引入一種新的 32 位基本類型(例如,char32)。所有形式的 Char 序列均基于 UTF-16。
          • 在現(xiàn)有的 16 位類型 char 的基礎(chǔ)上,為字符引入一種新的 32 位基本類型(例如,char32)。StringStringBuffer 接受并行 API,并將它們解釋為 UTF-16 序列或 UTF-32 序列;其他 char 序列繼續(xù)基于 UTF-16。
          • 使用 int 表示增補(bǔ)的代碼點(diǎn)。StringStringBuffer 接受并行 API,并將它們解釋為 UTF-16 序列或 UTF-32 序列;其他 char 序列繼續(xù)基于 UTF-16。
          • 使用代理 char 對,表示增補(bǔ)代碼點(diǎn)。所有形式的 char 序列基于 UTF-16。
          • 引入一種封裝字符的類。StringStringBuffer 接受新的 API,并將它們解釋為此類字符的序列。
          • 使用一個 CharSequence 實(shí)例和一個索引的組合表示代碼點(diǎn)。

          在這些方法中,一些在早期就被排除了。例如,重新定義基本類型 char,使其具有 32 位,這對于全新的平臺可能會非常有吸引力,但是,對于 J2SE 來說,它會與現(xiàn)有的 Java 虛擬機(jī)1、序列化和其他接口不兼容,更不用說基于 UTF-32 的字符串要使用兩倍于基于 UTF-16 的字符串的內(nèi)存了。添加一種新類型的 char32 可能會簡單一些,但是仍然會出現(xiàn)虛擬機(jī)和序列化方面的問題。而且,語言更改通常需要比 API 更改有更長的提前期,因此,前面兩種方法會對增補(bǔ)字符支持帶來無法接受的延遲。為了在余下的方法中篩選出最優(yōu)方案,實(shí)現(xiàn)小組使用四種不同的方法,在大量進(jìn)行低層字符處理的代碼(java.util.regex 包)中實(shí)現(xiàn)了對增補(bǔ)字符支持,并對這四種方法的難易程度和運(yùn)行表現(xiàn)進(jìn)行了比較。

          最終,專家組確定了一種分層的方法:

          • 使用基本類型 int 在低層 API 中表示代碼點(diǎn),例如 Character 類的靜態(tài)方法。
          • 將所有形式的 char 序列均解釋為 UTF-16 序列,并促進(jìn)其在更高層級 API 中的使用。
          • 提供 API,以方便在各種 char 和基于代碼點(diǎn)的表示法之間的轉(zhuǎn)換。

          在需要時,此方法既能夠提供一種概念簡明且高效的單個字符表示法,又能夠充分利用通過改進(jìn)可支持增補(bǔ)字符的現(xiàn)有 API。同時,還能夠促進(jìn)字符序列在單個字符上的應(yīng)用,這一點(diǎn)一般對于國際化的軟件很有好處。

          在這種方法中,一個 char 表示一個 UTF-16 代碼單元,這樣對于表示代碼點(diǎn)有時并不夠用。您會注意到,J2SE 技術(shù)規(guī)范現(xiàn)在使用術(shù)語代碼點(diǎn)和 UTF-16 代碼單元(表示法是相關(guān)的)以及通用術(shù)語字符(表示法與該討論沒有關(guān)系)。API 通常使用名稱 codePoint 描述表示代碼點(diǎn)的類型 int 的變量,而 UTF-16 代碼單元的類型當(dāng)然為 char

          我們將在下面兩部分中了解到 J2SE 平臺的實(shí)質(zhì)變化 — 其中一部分介紹單個代碼點(diǎn)的低層 API,另一部分介紹采用字符序列的高層接口。

          開放的增補(bǔ)字符:基于代碼點(diǎn)的 API

          新增的低層 API 分為兩大類:用于各種 char 和基于代碼點(diǎn)的表示法之間轉(zhuǎn)換的方法和用于分析和映射代碼點(diǎn)的方法。

          最基本的轉(zhuǎn)換方法是 Character.toCodePoint(char?high, char?low)(用于將兩個 UTF-16 代碼單元轉(zhuǎn)換為一個代碼點(diǎn))和 Character.toChars(int?codePoint)(用于將指定的代碼點(diǎn)轉(zhuǎn)換為一個或兩個 UTF-16 代碼單元,然后封裝到一個 char[] 內(nèi)。不過,由于大多數(shù)情況下文本以字符序列的形式出現(xiàn),因此,另外提供 codePointAtcodePointBefore 方法,用于將代碼點(diǎn)從各種字符序列表示法中提取出來:Character.codePointAt(char[]?a, int?index)String.codePointBefore(int index) 是兩種典型的例子。在將代碼點(diǎn)插入字符序列時,大多數(shù)情況下均有一些針對 StringBufferStringBuilder 類的 appendCodePoint(int codePoint) 方法,以及一個用于提取表示代碼點(diǎn)的 int[]String 構(gòu)建器。

          幾種用于分析代碼單元和代碼點(diǎn)的方法有助于轉(zhuǎn)換過程:Character 類中的 isHighSurrogateisLowSurrogate 方法可以識別用于表示增補(bǔ)字符的 char 值;charCount(int codePoint) 方法可以確定是否需要將某個代碼點(diǎn)轉(zhuǎn)換為一個或兩個 char

          但是,大多數(shù)基于代碼點(diǎn)的方法均能夠?qū)λ?Unicode 字符實(shí)現(xiàn)基于 char 的舊方法對 BMP 字符所實(shí)現(xiàn)的功能。以下是一些典型例子:

          • Character.isLetter(int codePoint) 可根據(jù) Unicode 標(biāo)準(zhǔn)識別字母。
          • Character.isJavaIdentifierStart(int codePoint) 可根據(jù) Java 語言規(guī)范確定代碼點(diǎn)是否可以啟動標(biāo)識符。
          • Character.UnicodeBlock.of(int codePoint) 可搜索代碼點(diǎn)所屬的 Unicode 字符子集。
          • Character.toUpperCase(int codePoint) 可將給定的代碼點(diǎn)轉(zhuǎn)換為其大寫等值字符。盡管此方法能夠支持增補(bǔ)字符,但是它仍然不能解決根本的問題,即在某些情況下,逐個字符的轉(zhuǎn)換無法正確完成。例如,德文字符“"?"”應(yīng)該轉(zhuǎn)換為“SS”,這需要使用 String.toUpperCase 方法。

          注意大多數(shù)接受代碼點(diǎn)的方法并不檢查給定的 int 值是否處于有效的 Unicode 代碼點(diǎn)范圍之內(nèi)(如上所述,只有 0x0 至 0x10FFFF 之間的范圍是有效的)。在大多數(shù)情況下,該值是以確保其有效的方法產(chǎn)生的,在這些低層 API 中反復(fù)檢查其有效性可能會對系統(tǒng)性能造成負(fù)面的影響。在無法確保有效性的情況下,應(yīng)用程序必須使用 Character.isValidCodePoint 方法確保代碼點(diǎn)有效。大多數(shù)方法對于無效的代碼點(diǎn)采取的行為沒有特別加以指定,不同的實(shí)現(xiàn)可能會有所不同。

          API 包含許多簡便的方法,這些方法可使用其他低層的 API 實(shí)現(xiàn),但是專家組覺得,這些方法很常用,將它們添加到 J2SE 平臺上很有意義。不過,專家組也排除了一些建議的簡便方法,這給我們提供了一次展示自己實(shí)現(xiàn)此類方法能力的機(jī)會。例如,專家組經(jīng)過討論,排除了一種針對 String 類的新構(gòu)建器(該構(gòu)建器可以創(chuàng)建一個保持單個代碼點(diǎn)的 String)。以下是使應(yīng)用程序使用現(xiàn)有的 API 提供功能的一種簡便方法:

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

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

          /**
           * 創(chuàng)建僅含有指定代碼點(diǎn)的新 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,則可能希望編寫一個重復(fù)使用 toChars 方法所使用的數(shù)列的通用版本:

          /**
           * 創(chuàng)建每一個均含有一個指定
           * 代碼點(diǎn)的新 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),您需要的是一個完全不同的解決方案。新的構(gòu)建器 String(int codePoint) 實(shí)際上建議作為 String.valueOf(char) 的一個基于代碼點(diǎn)的備選方案。在很多情況下,此方法用于消息生成的環(huán)境,例如:

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

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

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

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

          增補(bǔ)字符透視:功能增強(qiáng)

          在支持使用增補(bǔ)字符的 Java 2 平臺中的大部分更改沒有反映到新的 API 內(nèi)。一般預(yù)期是,處理字符序列的所有接口將以適合其功能的方式處理增補(bǔ)字符。本部分著重講述為達(dá)到此預(yù)期所作一些功能增強(qiáng)。

          Java 編程語言中的標(biāo)識符

          Java 語言規(guī)范指出所有 Unicode 字母和數(shù)字均可用于標(biāo)識符。許多增補(bǔ)字符是字母或數(shù)字,因此 Java 語言規(guī)范已經(jīng)參照新的基于代碼點(diǎn)的方法進(jìn)行更新,以在標(biāo)識符內(nèi)定義合法字符。為使用這些新方法,需要檢測標(biāo)識符的 javac 編譯器和其他工具都進(jìn)行了修訂。

          庫內(nèi)的增補(bǔ)字符支持

          許多 J2SE 庫已經(jīng)過增強(qiáng),可以通過現(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)行整理處理時,會將增補(bǔ)字符看作完整單元。
          • java.text.Bidi 類已更新,可以處理增補(bǔ)字符和 Unicode 4.0 中新增的其他字符。請注意,Cypriot Syllabary 字符子集內(nèi)的增補(bǔ)字符具有從右至左的方向性。
          • Java 2D API 內(nèi)的字體渲染和打印技術(shù)已經(jīng)過增強(qiáng),可以正確渲染和測量包含增補(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)換。對于 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 編程語言源文件中,如果使用可以直接表示增補(bǔ)字符的字符編碼,則使用增補(bǔ)字符最為方便。UTF-8 是最佳的選擇。在所使用的字符編碼無法直接表示字符的情況下,Java 編程語言提供一種 Unicode 轉(zhuǎn)義符語法。此語法沒有經(jīng)過增強(qiáng),無法直接表示增補(bǔ)字符。而是使用兩個連續(xù)的 Unicode 轉(zhuǎn)義符將其表示為 UTF-16 字符表示法中的兩個編碼單元。例如,字符 U+20000 寫作“\uD840\uDC00”。您也許不愿意探究這些轉(zhuǎn)義序列的含義;最好是寫入支持所需增補(bǔ)字符的編碼,然后使用一種工具(如 native2ascii)將其轉(zhuǎn)換為轉(zhuǎn)義序列。

          遺憾的是,由于其編碼問題,屬性文件仍局限于 ISO 8859-1(除非您的應(yīng)用程序使用新的 XML 格式)。這意味著您始終必須對增補(bǔ)字符使用轉(zhuǎn)義序列,而且可能要使用不同的編碼進(jìn)行編寫,然后使用諸如 native2ascii 的工具進(jìn)行轉(zhuǎn)換。

          經(jīng)修訂的 UTF-8

          Java 平臺對經(jīng)修訂的 UTF-8 已經(jīng)很熟悉,但是,問題是應(yīng)用程序開發(fā)人員在可能包含增補(bǔ)字符的文本和 UTF-8 之間進(jìn)行轉(zhuǎn)換時需要更加留神。需要特別注意的是,某些 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 和標(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 通過對其 UTF-16 表示法的兩個代理代碼單元單獨(dú)進(jìn)行編碼表示增補(bǔ)字符 。每個代理代碼單元由三個字節(jié)來表示,共有六個字節(jié)。而標(biāo)準(zhǔn) UTF-8 使用單個四字節(jié)序列表示整個字符。

          Java 虛擬機(jī)及其附帶的接口(如 Java 本機(jī)接口、多種工具接口或 Java 類文件)在 java.io.DataInputDataOutput 接口和類中使用經(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.InputStreamReaderOutputStreamWriter 類、java.nio.charset 設(shè)施 (facility) 以及許多其上層的 API 提供支持。

          由于經(jīng)修訂的 UTF-8 與標(biāo)準(zhǔn)的 UTF-8 不兼容,因此切勿同時使用這兩種版本的編碼。經(jīng)修訂的 UTF-8 只能與上述的 Java 接口配合使用。在任何其他情況下,尤其對于可能來自非基于 Java 平臺的軟件的或可能通過其編譯的數(shù)據(jù)流,必須使用標(biāo)準(zhǔn)的 UTF-8。需要使用標(biāo)準(zhǔn)的 UTF-8 時,則不能使用 Java 本機(jī)接口例程與經(jīng)修訂的 UTF-8 進(jìn)行轉(zhuǎn)換。

          在應(yīng)用程序內(nèi)支持增補(bǔ)字符

          現(xiàn)在,對大多數(shù)讀者來說最為重要的問題是:必須對應(yīng)用程序進(jìn)行哪些更改才能支持增補(bǔ)字符?

          答案取決于在應(yīng)用程序中進(jìn)行哪種類型的文本處理和使用哪些 Java 平臺 API。

          對于僅以各種形式 char 序列([char[]java.lang.CharSequence 實(shí)現(xiàn)、java.text.CharacterIterator 實(shí)現(xiàn))處理文本和僅使用接受和退回序列(如 char 序列)的 Java API 的應(yīng)用程序,可能根本不需要進(jìn)行任何更改。Java 平臺 API 的實(shí)現(xiàn)應(yīng)該能夠處理增補(bǔ)字符。

          對于本身解釋單個字符、將單個字符傳送給 Java 平臺 API 或調(diào)用能夠返回單個字符的方法的應(yīng)用程序,則需要考慮這些字符的有效值。在很多情況下,往往不要求支持增補(bǔ)字符。例如,如果某應(yīng)用程序搜索 char 序列中的 HTML 標(biāo)記,并逐一檢查每個 char,它會知道這些標(biāo)記僅使用 Basic Latin 字符子集中的字符。如果所搜索的文本含有增補(bǔ)字符,則這些字符不會與標(biāo)記字符混淆,因為 UTF-16 使用代碼單元表示增補(bǔ)字符,而代碼單元的值不會用于 BMP 字符。

          只有在某應(yīng)用程序本身解釋單個字符、將單個字符傳送給 Java 平臺 API 或調(diào)用能夠返回單個字符的方法且這些字符可能為增補(bǔ)字符時,才必須更改該應(yīng)用程序。在提供使用 char 序列的并行 API 時,最好轉(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ǔ)字符并同時簡化代碼(如上 格式化范例 中所述),則沒有必要這樣做。

          您可能會猶豫,是將所有文本轉(zhuǎn)換為代碼點(diǎn)表示法(即 int[])然后在該表示法中處理,還是在大多數(shù)情況下仍采用 char 序列,僅在需要時轉(zhuǎn)換為代碼點(diǎn),兩者之間孰優(yōu)孰劣很難確定。當(dāng)然,總體來說,Java 平臺 API 相對于 char 序列肯定具有一定的優(yōu)勢,而且采用 Java 平臺 API 可以節(jié)省內(nèi)存空間。

          對于需要與 UTF-8 之間進(jìn)行轉(zhuǎn)換的應(yīng)用程序,還需要認(rèn)真考慮是需要標(biāo)準(zhǔn)的 UTF-8 還是經(jīng)修訂的 UTF-8,并針對每種 UTF-8 采用適當(dāng)?shù)?Java 平臺。“經(jīng)修訂的 UTF-8”部分介紹進(jìn)行正確選擇所需的信息。

          使用增補(bǔ)字符測試應(yīng)用程序

          經(jīng)過前面部分的介紹后,無論您是否需要修訂應(yīng)用程序,測試應(yīng)用程序是否運(yùn)行正常始終是一種正確的做法。對于不含有圖形用戶界面的應(yīng)用程序,有關(guān)“在源文件內(nèi)表示增補(bǔ)字符” 的信息有助于設(shè)計測試用例。以下是有關(guān)使用圖形用戶界面進(jìn)行測試的補(bǔ)充信息。

          對于文本輸入,Java 2 SDK 提供用于接受“\Uxxxxxx”格式字符串的代碼點(diǎn)輸入方法,這里大寫的“U”表示轉(zhuǎn)義序列包含六個十六進(jìn)制數(shù)字,因此允許使用增補(bǔ)字符。小寫的“u”表示轉(zhuǎn)義序列“\uxxxx”的原始格式。您可以在 J2SDK 目錄 demo/jfc/CodePointIM 內(nèi)找到此輸入方法及其說明文檔。

          對于字體渲染,您需要至少能夠渲染一些增補(bǔ)字符的字體。其中一種此類字體為 James Kass 的 Code2001 字體,它提供手寫體字形(如 Deseret 和 Old Italic)。利用 Java 2D 庫中提供新功能,您只需將該字體安裝到 J2RE 的 lib/fonts/fallback 目錄內(nèi)即可,然后它可自動添加至在 2D 和 XAWT 渲染時使用的所有邏輯字體 — 無需編輯字體配置文件。

          至此,您就可以確認(rèn),您的應(yīng)用程序能夠完全支持增補(bǔ)字符了!

          結(jié)論

          對增補(bǔ)字符的支持已經(jīng)引入 Java 平臺,大部分應(yīng)用程序無需更改代碼即可處理這些字符。解釋單個字符的應(yīng)用程序可以在 Character 類和多種 CharSequence 子類中使用基于代碼點(diǎn)的新 API。

          鳴謝

          Java 平臺中的增補(bǔ)字符支持由 Java Community Process 的 JSR-204 專家組設(shè)計。技術(shù)規(guī)范設(shè)計主持為 Masayoshi Okutsu 和 Brian Beck (Sun Microsystems),其他專家組成員有 Craig Cummings (Oracle)、Mark Davis (IBM)、Markus Eble (SAP AG)、Jere K?pyaho (Nokia Corp.)、Kazuhiro Kazama (NTT)、Kenji Kazumura (Fujitsu Limited)、Eiichi Kimura (NEC Corp.)、Changshin Lee (Tmax Soft Inc.) 和 Toshiki Murata (Oki Electric Industry Co.)。參考實(shí)現(xiàn)由 Sun Microsystems 的 Java Internationalization 團(tuán)隊完成,并承蒙位于圣何塞的 IBM Globalization Center of Competency 的協(xié)助。技術(shù)規(guī)范的技術(shù)兼容套件為 Java Compatibility Kit,由 Sun Microsystems 的 JCK 團(tuán)隊實(shí)現(xiàn)。

          參考書目

          Masayoshi Okutsu, Brian Beck (ed.): Unicode Supplementary Character Support. Proposed Final Draft. Sun Microsystems, 2004.

          Java 2 Platform Standard Edition 5.0 API Specification. Sun Microsystems, 2004.

          The Unicode Consortium: The Unicode Standard, Version 4.0. Addison-Wesley, 2003.

          Ken Whistler, Mark Davis: Character Encoding Model. Unicode Technical Report #17. The Unicode Consortium, 2000.

          James Kass: Code2001, a Plane 1 Unicode-based Font.

          關(guān)于作者

          Norbert Lindenberg 是 Sun Microsystems 的 Java Web Services 團(tuán)隊內(nèi) Java Internationalization 技術(shù)主管。在加盟 Sun 之前,曾經(jīng)供職于 General Magic 和 Apple Computer,參與過多個國際化項目。他畢業(yè)于德國的卡爾斯魯厄大學(xué),擁有計算機(jī)科學(xué)理科碩士學(xué)位。

          Masayoshi Okutsu 是 Sun Microsystems 的 Java Web Services 團(tuán)隊的一名國際化工程師,目前擔(dān)任 Unicode Supplementary Character Support 的 Java Specification Request 204 的技術(shù)規(guī)范主管。在加盟 Sun Microsystems 之前,供職于 Digital Equipment Corporation,期間曾經(jīng)參與多個國際化項目。他畢業(yè)于日本山形大學(xué),擁有電子工程理學(xué)士學(xué)位。



          1 本網(wǎng)站中使用的術(shù)語“Java 虛擬機(jī)”或“JVM”是指針對 Java 平臺的虛擬機(jī)。
          posted on 2006-05-18 14:23 TrampEagle 閱讀(779) 評論(0)  編輯  收藏 所屬分類: 技術(shù)文摘
          主站蜘蛛池模板: 咸宁市| 板桥市| 清苑县| 西和县| 宜章县| 安西县| 广饶县| 宿迁市| 屏边| 怀来县| 玉环县| 宿松县| 云和县| 中牟县| 崇信县| 南乐县| 赤城县| 苍南县| 峡江县| 杨浦区| 文昌市| 玛纳斯县| 牙克石市| 邹城市| 邵东县| 桂平市| 绿春县| 拜泉县| 七台河市| 文安县| 航空| 五寨县| 兰西县| 涞源县| 镇巴县| 西吉县| 阿图什市| 翁源县| 湖南省| 佛学| 徐汇区|