字符編碼 代碼點

 
摘要
本文介 Java 平臺支持增字符的方式。增字符是 Unicode 準中代點超出 U+FFFF 的字符,因此它無法在 Java 言中描述爲單個的 16 體(例如char數據型)。些字符一般極少用,但是,有些會在如中文或日文人名中用到,因此,在東亞國家,政府用程序通常會要求支持些字符。
Java 平臺目前正在改,以便支持字符的理,這種進對現有的用程序影響微乎其微。新的低 API 在需要使用個的字符運行。不,大多數文本 API 均使用字符序列,例如String或字符數在,些均解釋爲 UTF-16 序列,而且, API 實現轉變爲正確地理增字符。些改已融入 Java 2 平臺 5.0 版,準版 (J2SE)
詳細釋這些改之外,本文同時爲應用程序開發確定和實現必要的更改提供指,以支持整個 Unicode 字符集的使用。
背景
Unicode 最初設計是作固定度的 16 位字符編碼。在 Java 言中,基本數據char初衷是通提供一種簡單的、能包含任何字符的數據型來充分利用這種設計點。不在看來,16 編碼的所有 65,536 個字符並不能完全表示全世界所有正在使用或曾使用的字符。於是,Unicode 準已展到包含多達 1,112,064 個字符。那些超出原來的 16 位限制的字符被稱作增字符。Unicode 2.0 版是第一個包含啓用增字符設計的版本,但是,直到 3.1 版才收入第一批增字符集。由於 J2SE 5.0 版必支持 Unicode 4.0 版,因此它必支持增字符。
字符的支持也可能會成爲東亞的一個普遍商要求。政府用程序會需要些增字符,以正確表示一些包含罕中文字符的姓名。出版用程序可能會需要些增字符,以表示所有的古代字符和體字符。中國政府要求支持 GB18030(一種對整個 Unicode 字符集編碼的字符編碼標準),因此,如果是 Unicode 3.1 版或更新版本,將包括增字符。臺灣 CNS-11643 包含的多字符在 Unicode 3.1 中列字符。香港政府定了一種針對的字符集,其中的一些字符是 Unicode 中的增字符。最後,日本的一些供商正劃利用增字符空中大量的用空收入 50,000 多個日文字字符體,以便從其有系遷移至基於 Java 平臺的解決方案。
因此,Java 平臺不需要支持增字符,而且必使用程序能方便地做到一點。由於增字符打破了 Java 言的基礎設計構想,而且可能要求對編程模型行根本性的修改,因此,Java Community Process 召集了一個,以期找到一個適當的解決方案。被稱 JSR-204 ,使用Unicode 增補字符支持的 Java 技術規範請求號。從技上來該專的決定適用於 J2SE 平臺,但是由於 Java 2 平臺企 (J2EE) J2SE 平臺的最上,因此它可以直接受益,我期望 Java 2 平臺袖珍版 (J2ME) 的配置也採用相同的設計方法。
,在瞭解 JSR-204 確定的解決方案之前,我需要先理解一些術語
點、字符編碼方案、UTF-16些是指什
不幸的是,引入增字符使字符模型得更加複雜了。在去,我可以簡單字符,在一個基於 Unicode 境(例如 Java 平臺)中,假定字符有 16 位,而在我需要更多的術語。我會盡量介得相對簡單一些如需瞭解所有詳細討論信息,您可以閱讀Unicode 標準第 2 章 Unicode 術報 17“字符編碼模型Unicode 專業人士可略所有介直接參本部分中的最後定
字符是抽象的最小文本位。它沒有固定的形狀(可能是一個字形),而且沒有“A”是一個字符,“€”(德國、法國和多其他歐洲國家通用貨幣志)也是一個字符。
字符集是字符的集合。例如,字字符是中國人最先明的字符,在中文、日文文和越南文的寫中使用。
編碼字符集是一個字符集,它爲每一個字符分配一個唯一數字。Unicode 準的核心是一個編碼字符集,字母“A”編碼爲 004116 和字符“€”編碼爲20AC16Unicode 準始使用十六制數字,而且在在前面加上前“U+”,所以“A”編碼書“U+0041”
點是指可用於編碼字符集的數字。編碼字符集定一個有效的代點範,但是並不一定將字符分配所有些代點。有效的 Unicode 點範 U+0000 U+10FFFFUnicode 4.0 將字符分配一百多萬個代點中的 96,382 點。
字符是代點在 U+10000 U+10FFFF 的字符,也就是那些使用原始的 Unicode 16 設計無法表示的字符。從 U+0000 U+FFFF 的字符集有候被稱基本多言面 (BMP)。因此,一個 Unicode 字符要屬於 BMP,要屬於增字符。
字符編碼方案是從一個或多個編碼字符集到一個或多個固定度代碼單元序列的映射。最常用的代碼單元是字,但是 16 位或 32 位整數也可用於內部理。UTF-32UTF-16 UTF-8 Unicode 準的編碼字符集的字符編碼方案。
UTF-32 即將一個 Unicode 點表示相同 32 位整數。很明,它是內部理最方便的表達方式,但是,如果作一般字符串表達方式,要消耗更多的內存。
UTF-16 使用一個或兩個未分配的 16 位代碼單元的序列 Unicode 編碼 U+0000 U+FFFF 編碼爲一個相同 16 元。增字符編碼爲兩個代碼單元,第一個元來自於高代理U+D800 U+DBFF),第二個元來自於低代理範U+DC00 U+DFFF)。在概念上可能看起來似於多字節編碼,但是其中有一個重要區 U+D800 U+DFFF 保留用於 UTF-16;沒有分配字符作點。意味着,於一個字符串中的獨的代碼單元,件可以識別是否碼單元表示某個單單元字符,或者是否碼單元是某個雙元字符的第一個或第二元。相當於某些傳統的多字字符編碼是一個著的改,在傳統的多字字符編碼中,字節值 0x41 既可能表示字母“A”,也可能是一個雙字字符的第二個字
UTF-8 使用一至四個字的序列對編碼 Unicode 編碼U+0000 U+007F 使用一個字節編碼U+0080 U+07FF 使用兩個字U+0800 U+FFFF 使用三個字,而 U+10000 U+10FFFF 使用四個字UTF-8 設計原理:字節值 0x00 0x7F 表示代 U+0000 U+007FBasic Latin 字符子集,它對應 ASCII 字符集)。些字節值不會表示其他代點,一特性使 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實現(例如String),和java.text.CharacterIterator實現
這麼術語。它與在 Java 平臺中支持增字符有什麼關系呢?
Java 平臺中增字符的設計方法
JSR-204 作出的主要決定是如何在 Java API 中表示增字符,包括個字符和所有形式的字符序列。並排除了多方法:
*                   重新定基本char,使其具有 32 位,這樣也會使所有形式的char序列成 UTF-32 序列。
*                   有的 16 char的基上,字符引入一新的 32 位基本型(例如,char32)。所有形式的 Char 序列均基於 UTF-16
*                   有的 16 char的基上,字符引入一新的 32 位基本型(例如,char32)。StringStringBuffer接受並行 API,並將它釋爲 UTF-16 序列或 UTF-32 序列;其他char序列繼續基於 UTF-16
*                   使用int表示增的代點。StringStringBuffer接受並行 API,並將它釋爲 UTF-16 序列或 UTF-32 序列;其他char序列繼續基於 UTF-16
*                   使用代理char,表示增點。所有形式的char序列基於 UTF-16
*                   引入一封裝字符的StringStringBuffer接受新的 API,並將它釋爲字符的序列。
*                   使用一個CharSequence例和一個索引的合表示代點。
些方法中,一些在早期就被排除了。例如,重新定基本char,使其具有 32 位,這對於全新的平臺可能會非常有吸引力,但是, J2SE ,它會與有的 Java 1、序列化和其他接口不兼容,更不用基於 UTF-32 的字符串要使用兩倍於基於 UTF-16 的字符串的內存了。添加一型的char32可能會簡單一些,但是仍然會出機和序列化方面的問題。而且,言更改通常需要比 API 更改有更的提前期,因此,前面兩方法會字符支持來無法接受的延了在餘下的方法中篩選出最方案,實現使用四不同的方法,在大量行低字符理的代java.util.regex包)中實現字符支持,並對這方法的易程度和運行表現進行了比
確定了一的方法:
*                   使用基本int在低 API 中表示代點,例如Character的靜方法。
*                   將所有形式的char序列均解釋爲 UTF-16 序列,並促其在更高層級 API 中的使用。
*                   提供 API,以方便在各char和基於代點的表示法之轉換
在需要,此方法既能提供一概念明且高效的個字符表示法,又能充分利用通可支持增字符的 API。同字符序列在個字符上的用,一點一般於國化的件很有好
這種方法中,一個char表示一個 UTF-16 碼單元,這樣對於表示代點有並不用。您會注意到,J2SE 術規在使用術語點和 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[]內。不,由於大多數情況下文本以字符序列的形式出,因此,另外提供codePointAtcodePointBefore方法,用於將代點從各字符序列表示法中提取出來:Character.codePointAt(char[] a, int index)String.codePointBefore(int index)是兩典型的例子。在將代點插入字符序列,大多數情況下均有一些針對StringBufferStringBuilderappendCodePoint(int codePoint)方法,以及一個用於提取表示代點的int[]String構建器。
用於分析代碼單元和代點的方法有助於轉換過程:Character 中的isHighSurrogateisLowSurrogate方法可以識別用於表示增字符的charcharCount(int codePoint)方法可以確定是否需要將某個代轉換爲一個或兩個char
但是,大多數基於代點的方法均能夠對所有 Unicode 字符實現基於char的舊方法 BMP 字符所實現的功能。以下是一些典型例子:
*                   Character.isLetter(int codePoint)可根據 Unicode 識別字母。
*                   Character.isJavaIdentifierStart(int codePoint)可根據 Java 範確定代點是否可以啓動標識
*                   Character.UnicodeBlock.of(int codePoint)可搜索代點所屬的 Unicode 字符子集。
*                   Character.toUpperCase(int codePoint)可將定的代轉換爲其大寫等字符。儘管此方法能支持增字符,但是它仍然不能解決根本的問題,即在某些情況下,逐個字符的轉換無法正確完成。例如,德文字符“"ß"”應該轉換爲“SS”需要使用String.toUpperCase方法。
注意大多數接受代點的方法並不檢查給定的int是否於有效的 Unicode 點範之內(如上所述,只有 0x0 0x10FFFF 的範是有效的)。在大多數情況下,該值是以確保其有效的方法生的,在些低 API 中反複檢查其有效性可能會性能造成面的影響。在無法確保有效性的情況下,用程序必使用Character.isValidCodePoint方法確保代點有效。大多數方法於無效的代點採取的行沒有特加以指定,不同的實現可能會有所不同。
API 包含便的方法,些方法可使用其他低 API 實現,但是組覺些方法很常用,將它添加到 J2SE 平臺上很有意。不也排除了一些建便方法,這給提供了一次展示自己實現方法能力的機會。例如,組經過討論,排除了一種針對String的新構建器(構建器可以建一個保持個代點的String)。以下是使用程序使用有的 API 提供功能的一種簡便方法:
/**
 * 含有指定代點的新 String。
 */
String newString(int codePoint) {
    return new String(Character.toChars(codePoint));
}
您會注意到,在簡單實現中,toChars方法始終創建一箇中數列,數列使用一次即立即棄。如果方法在您的性能估中出,您可能會希望將其爲針對普通的情況,即 BMP 字符:
/**
 * 含有指定代點的新 String
 * 針對 BMP 字符化的版本。
 */
String newString(int codePoint) {
    if (Character.charCount(codePoint) == 1) {
        return String.valueOf((char) codePoint);
    } else {
        return new String(Character.toChars(codePoint));
    }
}
或者,如果您需要多個這樣 string可能希望寫一個重使用toChars方法所使用的數列的通用版本:
/**
 * 一個均含有一個指定
 * 點的新 String。
 * 針對 BMP 字符化的版本。
 */
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;
}
,最您可能會發現,您需要的          是一個完全不同的解決方案。新的構建器String(int codePoint)實際上建String.valueOf(char)的一個基於代點的備選方案。在很多情況下,此方法用於消息生成的境,例如:
System.out.println("Character " + String.valueOf(char) + " is invalid.");
新的格式化 API支持增文字,提供一更加簡單備選方案:
System.out.printf("Character %c is invalid.%n", codePoint);
使用此高 API 僅簡捷,而它有很多特殊的點:它可以避免串(串會使消息很本地化),並將需要移進資源包 (resource bundle) 的字符串數量從兩個減少到一個。
字符透:功能增
在支持使用增字符的 Java 2 平臺中的大部分更改沒有反映到新的 API 內。一般期是,理字符序列的所有接口將以適合其功能的方式理增字符。本部分着重達到此期所作一些功能增
Java 言中的標識
Java 範指出所有 Unicode 字母和數字均可用於標識符。多增字符是字母或數字,因 Java 範已參照新的基於代點的方法行更新,以在標識符內定合法字符。使用些新方法,需要檢測標識符的 javac 編譯器和其他工具都行了修
內的增字符支持
J2SE 經過,可以通過現有接口支持增字符。以下是一些例子:
*                   字符串大小寫轉換功能已更新,可以理增字符,也可以實現 Unicode 準中定的特殊大小寫規則
*                   java.util.regex包已更新,這樣模式字符串和目字符串均可以包含增字符並將其作完整理。
*                   在,在java.text包內行整理,會將增字符看作完整元。
*                   java.text.Bidi已更新,可以理增字符和 Unicode 4.0 中新增的其他字符。注意,Cypriot Syllabary 字符子集內的增字符具有從右至左的方向性。
*                   Java 2D API 內的字體渲染和打印技經過,可以正確渲染和量包含增字符的字符串。
*                   Swing 文本實現已更新,可以理包含增字符的文本。
字符轉換
只有很少的字符編碼可以表示增字符。如果是基於 Unicode 編碼(如 UTF-8 UTF-16LE),舊版的 J2RE 內的字符轉換器已按照正確理增字符的方式實現轉換 J2RE 5.0,可以表示增字符的其他編碼轉換器已更新:GB18030x-EUC-TW實現所有 CNS 11643 面)和 Big5-HKSCS實現 HKSCS-2001)。
在源文件內表示增字符
Java 言源文件中,如果使用可以直接表示增字符的字符編碼使用增字符最方便。UTF-8 是最佳的選擇。在所使用的字符編碼無法直接表示字符的情況下,Java 言提供一 Unicode 轉義法。此法沒有經過,無法直接表示增字符。而是使用兩個連續 Unicode 轉義符將其表示 UTF-16 字符表示法中的兩個編碼單元。例如,字符 U+20000 寫作“/uD840/uDC00”。您也不願意探究轉義序列的含;最好是寫入支持所需增字符的編碼,然後使用一工具(如 native2ascii)將其轉換爲轉義序列。
憾的是,由於其編碼問題,屬性文件仍侷限於 ISO 8859-1(除非您的用程序使用新的 XML 格式)。意味着您始須對字符使用轉義序列,而且可能要使用不同的編碼進寫,然後使用 native2ascii 的工具轉換
UTF-8
Java 平臺對經 UTF-8 很熟悉,但是,問題用程序開發在可能包含增字符的文本和 UTF-8 間進轉換時需要更加留神。需要特注意的是,某些 J2SE 接口使用的編碼 UTF-8 相似但與其並不兼容。以前,此編碼被稱“Java modified UTF-8” Java UTF-8或(錯誤地)直接稱“UTF-8” J2SE 5.0,其明文檔正在更新,此編碼“modified UTF-8” UTF-8)。
UTF-8 UTF-8 之所以不兼容,其原因有兩點。其一, UTF-8 將字符 U+0000 表示雙字序列 0xC0 0x80,而 UTF-8 使用節值 0x0。其二, UTF-8 過對 UTF-16 表示法的兩個代理代碼單編碼表示增字符個代理代碼單元由三個字來表示,共有六個字。而 UTF-8 使用個四字序列表示整個字符。
Java 機及其附的接口(如 Java 本機接口、多工具接口或 Java 文件)在java.io.DataInputDataOutput接口和中使用 UTF-8 實現或使用些接口和,並行序列化。Java 本機接口提供與 UTF-8 間進轉換的例程。而 UTF-8 Stringjava.io.InputStreamReaderOutputStreamWriterjava.nio.charset (facility) 以及多其上 API 提供支持。
由於 UTF-8 準的 UTF-8 不兼容,因此切勿同使用版本的編碼 UTF-8 只能與上述的 Java 接口配合使用。在任何其他情況下,尤其於可能來自非基於 Java 平臺的件的或可能通編譯的數據流,必使用準的 UTF-8。需要使用準的 UTF-8 不能使用 Java 本機接口例程與 UTF-8 轉換
用程序內支持增字符
在,大多數者來重要的問題是:必須對應用程序行哪些更改才能支持增字符?
答案取決於在用程序中行哪種類型的文本理和使用哪些 Java 平臺 API
以各形式char序列([char[]java.lang.CharSequence實現java.text.CharacterIterator實現理文本和使用接受和退回序列(如char序列)的 Java API 用程序,可能根本不需要行任何更改。Java 平臺 API 實現應該夠處理增字符。
於本身解釋單個字符、將個字符 Java 平臺 API 調用能返回個字符的方法的用程序需要考慮這些字符的有效。在很多情況下,往往不要求支持增字符。例如,如果某用程序搜索char序列中的 HTML 標記,並逐一檢查每char,它會知道標記僅使用 Basic Latin 字符子集中的字符。如果所搜索的文本含有增字符,則這些字符不會與標記字符混淆,因 UTF-16 使用代碼單元表示增字符,而代碼單元的不會用於 BMP 字符。
只有在某用程序本身解釋單個字符、將個字符 Java 平臺 API 調用能返回個字符的方法且些字符可能字符,才必更改該應用程序。在提供使用char序列的並行 API ,最好而使用此 API。在其他情況下,有必要使用新的 API char和基於代點的表示法之間進轉換,並調用基於代點的 API。當然,如果您發現 J2SE 5.0 中有更新、更方便的 API,使您能支持增字符並同時簡化代(如上格式化範例中所述),沒有必要這樣做。
您可能會猶豫,是將所有文本轉換爲點表示法(即int[])然後在表示法中理,是在大多數情況下仍採用char序列,在需要時轉換爲點,兩者之孰劣很確定。當然,體來Java 平臺 API char序列肯定具有一定的優勢,而且採用 Java 平臺 API 可以省內存空
於需要與 UTF-8 間進轉換用程序,需要真考是需要準的 UTF-8 UTF-8,並針對每種 UTF-8 採用適當的 Java 平臺。經修訂的 UTF-8部分介紹進行正確選擇所需的信息。
使用增字符測試應用程序
經過前面部分的介後,無您是否需要修訂應用程序,測試應用程序是否運行正常始是一正確的做法。於不含有形用界面的用程序,有在源文件內表示增補字符 的信息有助於設計測試用例。以下是有使用形用界面測試充信息。
於文本入,Java 2 SDK提供用於接受“/Uxxxxxx”格式字符串的代入方法,裏大寫的“U”表示轉義序列包含六個十六制數字,因此允使用增字符。小寫的“u”表示轉義序列“/uxxxx”的原始格式。您可以在 J2SDK demo/jfc/CodePointIM 內找到此入方法及其明文檔。
於字體渲染,您需要至少能渲染一些增字符的字體。其中一字體 James Kass Code2001字體,它提供手寫體字形(如 Deseret Old Italic)。利用 Java 2D 中提供新功能,您只需將字體安裝到 J2RE lib/fonts/fallback 內即可,然後它可自添加至在 2D XAWT 渲染使用的所有邏輯字體無需編輯字體配置文件。
至此,您就可以確,您的用程序能完全支持增字符了!
結論
字符的支持已引入 Java 平臺,大部分用程序無需更改代即可些字符。解釋單個字符的用程序可以在Character和多CharSequence中使用基於代點的新 API
 
 
 
以下是UnicodeUTF-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 數組轉整數:
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;
}
整數轉byte數組:
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;
}
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章