Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Java プラットフォームにおける補助文字のサポート

 

2004 年 5 月

English: Supplementary Characters in the Java Platform
中文: Java 平台中的增补字符

要旨

この記事では、Java プラットフォームで補助文字がどのようにサポートされるかを説明します。補助文字 (supplementary character) とは、Unicode 規格で定義される文字のうち、コードポイントが U+FFFF を超えているものを指します。これらの文字は、Java プログラミング言語の char データ型のような単一の 16 ビットエンティティでは表現できません。補助文字が使用される機会は通常あまり多くありませんが、たとえば日本や中国の人名などで使われているため、一般的に東アジア諸国の政府関連業務などでは補助文字をサポートする必要があります。

Java プラットフォームは、既存のアプリケーションに対する影響を最小限に抑えながら補助文字を処理できるよう機能拡張されています。新しい低レベル API を使うと、必要に応じて個々の文字を個別に操作することができます。一方、ほとんどのテキスト処理 API では、String クラスや文字配列などの文字シーケンスが使用されます。現在、これらの文字シーケンスは UTF-16 シーケンスとして解釈されるようになっており、テキスト処理 API は補助文字を正しく処理できます。これらの拡張機能は、Java 2 Platform, Standard Edition(J2SE) バージョン 5.0 に組み込まれています。

この記事では、J2SE での拡張機能を詳しく説明するほか、アプリケーション開発者を対象として、完全な Unicode 文字セットを使用するために必要な変更を組み込むときのガイドラインを示します。

背景

もともと Unicode は固定長 16 ビットの文字エンコーディングとして設計されました。この設計に基づいて、Java プログラミング言語の基本データ型 char は任意の文字を保持できる単純なデータ型として設定されました。しかし、16 ビットのエンコーディングで表現できる 65,536 文字では世界中で使用されているすべての文字に対応するには不十分であることがまもなく判明し、最大 1,112,064 文字を表現できるように Unicode 規格が拡張されました。従来の 16 ビットのエンコーディング範囲を超える文字は、補助文字 と呼ばれています。補助文字をサポートするように設計された最初の Unicode 規格はバージョン 2.0 でしたが、実際に最初の補助文字が定義されたのはバージョン 3.1 でした。J2SE バージョン 5.0 では Unicode 4.0 をサポートしています。

東アジアのマーケットでは、補助文字のサポートが一般的な業務要件になりつつあります。たとえば、政府関連の適用業務では中国の珍しい漢字を含む名前などを正しく表示するために補助文字をサポートする必要があります。出版業界では、歴史上の文字や異体字などを完全に表現するために補助文字が必要になります。中国政府は、中国新文字コード規格 GB18030 のサポートを求めています。GB18030 は、Unicode 文字セット全体をエンコードする文字エンコード方式であり、Unicode3.1 以降を仮定したときは補助文字を含みます。台湾の規格 CNS-11643 には、Unicode 3.1 に補助文字として登録された数多くの文字が含まれています。香港政府は広東語に必要な文字セットを定義しましたが、これらの文字の一部は Unicode の補助文字です。また、日本のいくつものベンダーが、50,000 文字以上の漢字の異体字を表すために補助文字空間内の大きなプライベート領域を使用し、独自のシステムから Java プラットフォームに基づくソリューションに移行することを計画しています。

したがって、今日の Java プラットフォームには、補助文字をサポートする機能だけではなく、アプリケーション内部で補助文字を容易にサポートできるようにする機能も必要です。補助文字によって Java プログラミング言語の基本的な前提条件が崩され、プログラミングモデルの根本的な変更が必要になる可能性があります。この問題に対する適正なソリューションを選択するために、Java Community Process(JCP)のもとに専門家グループが召集されました。このグループは、Java Specification Request for Unicode Supplementary Character Support の番号に基づき、JSR-204 専門家グループと呼ばれています。厳密には JSR-204 専門家グループの決定事項はJ2SE プラットフォームだけに適用されますが、Java 2 Platform, Enterprise Edition (J2EE) は J2SE プラットフォーム上に実装されるため、JSR-204 によるメリットは J2EE にも直接もたらされます。また、Java 2 Platform, Micro Edition (J2ME) の構成でも同じ設計アプローチが採用される予定です。

JSR-204 専門家グループが提案するソリューションを検証する前に、いくつかの基本概念や技術用語について理解しておく必要があります。

コードポイント、文字エンコーディング方式、UTF-16 などの用語の意味

不都合なことに、補助文字の導入によって文字モデルが若干複雑になりました。従来の文字モデルは極めて単純であり、Java プラットフォームのような Unicode ベースの環境では、文字を 16 ビットで表現できました。しかし、現在はさらに複雑な表現技法(新しい文字モデル)が必要になっています。この記事では、新しい文字モデルの概要だけを簡単に説明します。詳しい説明については、Unicode Standard の第 2 章または Unicode 技術レポート 17「Character Encoding Model」 を参照してください。Unicode に精通している読者は、最後の定義の説明を除き、このセクションをスキップしてもかまいません。

文字 とは、テキストの概念的な最小単位で、固定の形状(グリフ によって表されます)や特定の値を持ちません。たとえば、"A" や ドイツ、フランス、その他のヨーロッパ諸国の通貨記号"€"などは文字です。

文字セット とは、複数の文字の集合です。たとえば、漢字は最初に中国で考案された文字セットで、これまで中国語、日本語、韓国語、ベトナム語で使われてきました。

コード化文字セット とは、個々の文字に一意の数値(コード)が割り当てられた文字セットです。たとえば、Unicode 規格の中核をなすコード化文字セットでは、文字 "A" に数値 004116 が、文字 "€" に数値 20AC16 が割り当てられています。Unicode 規格では常に 16 進数が使用され、数値の前に "U+" という接頭辞が付きます。たとえば、文字 "A" に対応する数値は "U+0041" と表記されます。

コードポイント とは、コード化文字セットで使用できる数値(コード)です。コード化文字セットでは 有効な コードポイントの範囲が定義されますが、すべてのコードポイントに文字が割り当てられるとは限りません。Unicode で使用できる有効なコードポイントの範囲は、U+0000 ~ U+10FFFF です。Unicode 4.0 では、これら 100 万個以上のコードポイントのうち、96,382 個のコードポイントに文字が割り当てられています。

補助文字 とは、U+10000 ~ U+10FFFF の範囲のコードポイントを持つ文字です。つまり、従来の 16 ビットの Unicode では表現できなかった文字です。これに対して、U+0000 ~ U+FFFF の範囲の文字は「基本多言語面 (Basic Multilingual Plane、BMP)」と呼ばれます。したがって、Unicode の各文字は、BMP または補助文字のどちらかに分類されます。

文字エンコーディング方式 とは、1 つ以上のコード化文字セットの数値(コード)から 1 つ以上の固定長 コード 単位のシーケンスへのマッピングです。最もよく使われるコード単位はバイトですが、16 ビットまたは 32 ビットの整数を内部処理に使用することもできます。UTF-32、UTF-16、UTF-8 は、Unicode 規格のコード化文字セット用の文字エンコーディング方式です。

UTF-32 では、単純に Unicode の個々のコードポイントを同じ値の 32 ビット整数として表します。UTF-32 は明らかに内部処理にとって最も簡便なエンコーディング方式ですが、汎用的な文字列表現として使用する場合には、必要以上にメモリが消費されます。

UTF-16 では、1 つまたは 2 つの符号なし 16 ビット コード単位のシーケンスを使って Unicode の個々のコードポイントをエンコードします。U+0000 ~ U+FFFF の範囲の文字 (BMP) は、同じ値を持つ 1 つの 16 ビットコード単位を使ってエンコードされます。補助文字は 2 つのコード単位を使ってエンコードされます。最初のコード単位は上位サロゲート領域 (U+D800 ~ U+DBFF) 、2 番目のコード単位は下位サロゲート領域 (U+DC00 ~ U+DFFF) 内の値になります。UTF-16 は概念的にはマルチバイト エンコーディング方式と似ていますが、重要な違いがあります。サロゲート領域 (U+D800 ~ U+DFFF) の値は UTF-16 の内部で使用するために予約されており、これらの値に文字が割り当てられることはありません (U+D800 ~ U+DFFF の値はコードポイントとして使用されません)。したがって、文字列内の個々のコード単位について、そのコード単位が 1 つのコード単位からなる文字を表しているのか、2 つのコード単位からなる文字の最初のコード単位または 2 番目のコード単位を表しているのかをソフトウェア側で判断することができます。これは従来のマルチバイト エンコーディング方式と比べて大きな改善といえます。従来の方式では、たとえば、0x41 というバイト値が文字 "A" を意味するのか、あるいは 2 バイト文字の 2 番目のバイトを表しているのかを判断できませんでした。

UTF-8 では、1 ~ 4 個のバイト(8 ビット)のシーケンスを使って、Unicode の個々のコードポイントをエンコードします。U+0000 ~ U+007F は 1 バイトでエンコードされます。U+0080 ~ U+07FF は 2 バイト、U+0800 ~ U+FFFF は 3 バイト、U+10000 ~ U+10FFFF は 4 バイトでそれぞれエンコードされます。UTF-8 では、0x00 ~ 0x7F のバイト値が常に U+0000 ~ U+007F のコードポイント (ASCII 文字セットに相当する基本ラテンブロック) を表します。これらのバイト値が他のコードポイントを表すことは決してありません。このような特性があるため、一定の ASCII 文字に特別な意味を割り当てるソフトウェアでも、UTF-8 を安心して使用できます。

次表に、各エンコーディング方式で数個の文字を表現した場合の比較を示します。

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

なお、この記事では、Java 2 プラットフォームで認識される文字シーケンスのすべてのコンテナをまとめて表現するために、「文字シーケンス」または「char シーケンス」という用語を使用します。これら文字シーケンスのコンテナとして、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 など) を新たに導入する。String および StringBuffer は、類似API を通じて文字シーケンスを UTF-16 シーケンスまたは UTF-32 シーケンスとして解釈する。その他の char シーケンスは UTF-16 で表現する。
  • int データ型を使用して補助文字のコードポイントを表現する。String および StringBuffer は、類似 API を通じて文字シーケンスを UTF-16 シーケンスまたは UTF-32 シーケンスとして解釈する。その他の char シーケンスは変更なく UTF-16 で表現する。
  • char 型のサロゲートペアを使用して補助文字のコードポイントを表現する。すべての形式の char シーケンスを UTF-16 で表現する。
  • 文字をカプセル化する新しいクラスを導入する。StringStringBuffer は、新しい API を通じてそのクラスで表現される文字のシーケンスとして認識する。
  • CharSequence のインスタンスとインデックスを組み合わせてコードポイントを表現する。

これらのアプローチの一部は早い時期に検討対象から除外されました。たとえば、基本データ型 char を再定義して 32 ビットにするというアプローチはまったく新しいプラットフォームには非常に魅力的ですが、J2SE では既存の Java Virtual Machine1(JVM)、直列化、その他のインタフェースとの互換性が損なわれます。また、UTF-32 ベースの文字列によって UTF-16 ベースの文字列の 2 倍のメモリが使用されることは言うまでもありません。char32 のような新しいデータ型を導入するというアプローチは比較的簡単ですが、JVM や直列化で若干の問題が発生することを避けられません。また、一般的に、言語の変更は API の変更よりも長いリードタイムが必要であるため、上記の 2 つのアプローチでは補助文字のサポートが大幅に遅れてしまう可能性があります。残りのアプローチの中から最適なものを選択するために、実装チームは、低レベルの文字処理 (java.util.regex パッケージ) を実行する多くのコードに対して補助文字のサポート機能を実際に組み込み、開発の容易さやランタイム性能の観点から各アプローチを比較・検証しました。

その結果、複数のアプローチを階層的に組み合わせた次のような複合アプローチを決定しました。

  • 基本データ型 int を使用して、低レベル API (Character クラスのスタティックメソッドなど) のコードポイントを表現する。
  • すべての形式の char シーケンスを UTF-16 シーケンスとして解釈し、高レベル API で UTF-16 シーケンスを使うようにする。
  • さまざまな char 型とコードポイントによる表現を相互に簡単に変換できる API を提供する。

このアプローチでは、個々の文字を必要に応じて概念的に簡潔かつ効率的に表現できると同時に、既存の API を変更して補助文字をサポートすることができます。また、個々の文字よりも文字シーケンスのほうが優先的に使用されるため、各国語に対応する必要のあるソフトウェアに適しています。

このアプローチでは 1 つの char が 1 つの UTF-16 コード単位を表しますが、一部のコードポイントは 1 つの UTF-16 コード単位では表現できません。J2SE バージョン 5.0 の仕様では、エンコード表現が問題となる場面では「コードポイント」および「UTF-16 コード単位」という用語が区別して使用され、それ以外の場面では汎用的な用語として「文字」が使用されています。通常、API では、コードポイントを表す場合に codePoint という名前の int 型変数を使用します。UTF-16 コード単位を表す場合には、当然ながら変数は char 型です。

次の 2 つのセクションでは、J2SE プラットフォーム上で補助文字をサポートするための実際の変更について説明します。最初のセクションは個々のコードポイントを操作する低レベル API に関する説明であり、2 番目のセクションは文字シーケンスを操作する高レベルインタフェースに関する説明です。

補助文字サポートのための変更 (1) : コードポイント用の API

新たに追加された低レベル API は、(1) さまざまな char 型による表現とコードポイントによる表現を相互に変換するメソッド、(2) コードポイントの分析とマッピングを実行するメソッドという 2 つの大きなカテゴリに分けられます。

最も基本的な変換メソッドは、2 つの UTF-16 コード単位を 1 つのコードポイントに変換する Character.toCodePoint(char high, char low)、および、渡されたコードポイントを 1 つまたは 2 つの UTF-16 コード単位に変換して char[] にラップする Character.toChars(int codePoint) です。ただし、ほとんどの場合、テキストは文字シーケンスの形式で渡されるため、さまざまな文字シーケンスから 1 つのコードポイントを抽出する codePointAt メソッドと codePointBefore メソッドが用意されています。このようなメソッドの代表例は、Character.codePointAt(char[] a, int index) および String.codePointBefore(int index) です。また、コードポイントを 1 つの文字シーケンスに入れるという一般的な処理を実行するために、StringBuffer クラスと StringBuilder クラスで使用できる appendCodePoint(int codePoint) メソッド、および、コードポイントを表す整数配列 int[] を引数とする String コンストラクタが用意されています。

変換処理時に役立つ、コード単位とコードポイントを分析するためのメソッドもあります。Character クラスの isHighSurrogate メソッドと isLowSurrogate メソッドは、補助文字の表現に使用される char 型の値を検査します。charCount(int codePoint) メソッドは、コードポイントを 1 つの char に変換すればよいのか、2 つの char に変換すればよいのかを判断します。

なお、コードポイント用のほとんどのメソッドは、従来の char 用のメソッドが BMP 文字に対して実行していた機能を Unicode 文字セット全体に対して実行します。代表的な例を次に示します。

  • 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 プラットフォームに追加するのが適切であると JSR-204 専門家グループが判断したものです。一方、候補となった便利なメソッドのうち、JSR-204 専門家グループが API から除外したものもあります。ここでは、そのようなメソッドをユーザ自身が実装する方法を示します。たとえば、JSR-204 専門家グループは、検討の結果 String クラス用の新しいコンストラクタを API から除外しました。このコンストラクタは、1 つのコードポイントを保持する 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 大量生産用のメソッドを作成することもできます。

/**
 * 指定されたコードポイントを保持する 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.valueOf(char) メソッドは次のようにメッセージを生成するときに使用されます。この valueOf(char) メソッドに対し、int 型のコードポイント対応版として String(int codePoint) コンストラクタが提案されましたが、採用はされませんでした。

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

このような場合、新しい書式化 API を使う方がずっと簡潔な解決方法となります(この API は補助文字もサポートしています)。

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

この高レベル API を使用すると、コードが簡単になるだけではなく、別のいくつかのメリットを享受できます。メッセージの作成時に文字列を連結すると、メッセージのローカライズ(地域化)が極めて困難になります。上記の API を使用すると、文字列の連結を避けることができます。また、リソースバンドルに入れる文字列の数が 2 つから 1 つに減ります。

補助文字サポートのための変更 (2) : 文字シーケンスを取り扱うための機能拡張

補助文字を使用可能にするための Java 2 プラットフォームへの変更の多くは、新しい API の追加ではなく既存の API への変更という形でなされています。文字シーケンスを取り扱うすべてのインタフェースの一般的な必要条件として、それぞれの機能に合った方法で補助文字を操作できるようにする必要があります。ここでは、このような要件を満たすための機能拡張について説明します。

Java プログラミング言語の識別子

Java 言語仕様では、Unicode のすべての文字と数字が Java 識別子の中で使用できると規定されています。ほとんどの補助文字は文字または数字であるため、新しいコードポイント用メソッドを参照して識別子の中の有効な文字を定義するように、Java 言語仕様が変更されました。識別子を検出する必要のある javac コンパイラとその他のツールは、これらの新しいメソッドを使用するように変更されました。

ライブラリにおける補助文字のサポート

既存のインタフェースを通じて補助文字をサポートするために、多くの J2SE ライブラリの機能が拡張されました。いくつかの例を次に示します。

  • 文字列の大文字/小文字変換機能が拡張され、補助文字がサポートされるようになりました。また、Unicode 規格で定義された特別な変換規則が組み込まれました。
  • java.util.regex パッケージが更新され、パターン文字列とターゲット文字列に補助文字を使用できるようになりました。補助文字は完全なコード単位として取り扱われます。
  • java.text パッケージの Collation で補助文字が完全なコード単位として取り扱われるようになりました。
  • java.text.Bidi クラスが更新され、Unicode 4.0 の新しい文字と補助文字を取り扱えるようになりました。Cypriot Syllabary ブロック内の補助文字は右から左の方向性を持つものとして扱われることに注意してください。
  • Java 2D API 内でのフォントの表示/印刷機能が拡張され、補助文字を含む文字列が正しく表示/印刷できるようになりました。
  • Swing テキストコンポーネントの実装が更新され、補助文字を含むテキストを操作できるようになりました。

文字の変換

補助文字を表現できる文字エンコーディング方式の数は限られています。UTF-8 や UTF-16LE などの Unicode ベースのエンコーディング方式では、補助文字を正しく取り扱える変換機能が旧バージョンの J2RE の文字コンバータにすでに組み込まれていました。J2RE 5.0 では、補助文字を表現できる他のエンコーディング方式用のコンバータが更新されました。これらのコンバータは、GB18030、x-EUC-TW (CNS 11643 のすべての面を含む)、Big5-HKSCS (HKSCS-2001) 文字セットに対応しています。

ソースファイル内での補助文字の表現

補助文字を直接表現できる文字エンコーディング方式を採用すれば、Java プログラミング言語のソースファイル内で補助文字をきわめて簡単に取り扱うことができます。たとえば、UTF-8 は最適なエンコーディング方式です。補助文字を直接表現できない文字エンコーディング方式を使用する場合に備えて、Java プログラミング言語には Unicode エスケープ構文が用意されています。この Unicode エスケープ構文には、補助文字を直接表現する機能は追加されていません。その代わり、文字を UTF-16 で表現した場合の 2 つのコード単位に対応する 2 つの連続した Unicode エスケープによって補助文字が表されます。たとえば、補助文字 U+20000 は "\uD840\uDC00" と表されます。通常、これらのエスケープシーケンスをユーザが直接書くのは困難です。そのため、必要な補助文字をサポートするエンコーディング方式で文字を書いた後、native2ascii などの変換ツールを使用して文字をエスケープシーケンスに変換することをお勧めします。

不都合なことに、プロパティファイルについては、アプリケーション内で新しい XML フォーマットを使用しない限り、エンコーディング方式が ISO 8859-1 に限定されます。したがって、補助文字を表すには常にエスケープシーケンスを使用する必要があります。この場合にも、必要な補助文字をサポートするエンコーディング方式で文字を書いた後、native2ascii などの変換ツールを使用して文字をエスケープシーケンスに変換してください。

Modified UTF-8

Java プラットフォームにとって "Modified UTF-8" は新しいエンコーディング方式ではありません。アプリケーション開発者は、補助文字を含むテキストを標準 UTF-8 との間で変換するにあたって、Modified UTF-8 の特性を十分に把握しておく必要があります。特に注意を要する点は、一部の J2SE インタフェースにおいて、UTF-8 と類似しているものの、UTF-8 と互換性がないエンコーディング方式が使用されていることです。このエンコーディング方式は過去に、"Java modified UTF-8" または (誤って) 単に "UTF-8" と呼ばれていました。J2SE 5.0 では、ドキュメントが更新され、"Modified UTF-8" という呼称に統一されています。

Modified UTF-8 と標準 UTF-8 との非互換性は、2 つの相違点に起因しています。最初の相違点は、Modified UTF-8 では文字 U+0000 が 2 バイトのシーケンス 0xC0 0x80 で表現されるのに対して、標準 UTF-8 では 1 バイトの値 0x0 で表現されることです。2 番目の相違点として、Modified UTF-8 では、補助文字を UTF-16 で表現した場合の 2 つのサロゲートコード単位を別々にエンコードすることによって補助文字が表されます。個々の代用コード単位は 3 バイトで表されます。つまり、1 つの補助文字を表すために 6 バイトが使用されます。一方、標準 UTF-8 では、単一の 4 バイト シーケンスを使用して 1 つの補助文字を表します。

Modified UTF-8 が使われる箇所は、Java Virtual Machine (JVM) および JVM に関連するインタフェース (Java Native Interface、さまざまなツールインタフェース、Java クラスファイルなど)、java.io.DataInput インタフェースと DataOutput インタフェースおよびこれらのインタフェースを使用/実装するクラス、そして直列化です。Java Native Interface には、Modified UTF-8 との間で変換を実行するルーチンがあります。一方、標準 UTF-8 が使用される箇所は、String クラス、java.io.InputStreamReader クラスと OutputStreamWriter クラス、java.nio.charset ユーティリティ、およびこれらのクラスを使用する多くの API です。

Modified UTF-8 と標準 UTF-8 は相互に互換性がないため、これら 2 種類のエンコーディング方式は厳密に使い分ける必要があります。Modified UTF-8 は上記の Java インタフェースだけで使用できます。それ以外の場合、特に Java プラットフォームと関連のないソフトウェアによって取り扱われる可能性のあるデータストリームでは、標準 UTF-8 を使用する必要があります。標準 UTF-8 を使用する必要がある場合、Modified UTF-8 との間で変換を実行する Java Native Interface ルーチンは使用できません。

ユーザアプリケーション内での補助文字のサポート

ここで、数多くの読者にとって最も興味のある問題「補助文字をサポートするために、どのような変更をアプリケーションに加えればよいのか」を説明します。

この問題に対する回答は、アプリケーション内部で実行するテキスト処理の内容と、使用する Java アプリケーション API のタイプに応じて異なります。

さまざまな形式の char シーケンス (char[]java.lang.CharSequence の実装、java.text.CharacterIterator の実装) としてのみテキストを操作し、そのような char シーケンスを入出力引数とする Java API だけを使用するアプリケーションについては、一般的に変更を加える必要はありません。Java プラットフォーム API の実装によって補助文字が自動的に処理されます。

個々の文字を独自に解釈するアプリケーション、個々の文字を Java プラットフォーム API に渡すアプリケーション、個々の文字を返すメソッドを呼び出すアプリケーションでは、それらの文字の値が有効かどうかをチェックする必要があります。ただし、ほとんどの場合、補助文字をサポートするための変更は不要です。たとえば、char シーケンスをスキャンして HTML タグを検出するアプリケーションの場合、HTML タグに含まれる各文字 (char) を個別にチェックすれば、それらの HTML タグで基本ラテンブロックの文字だけが使用されていることがわかります。スキャン対象のテキストに補助文字が含まれている場合でも、それらの補助文字がタグの文字と混同される可能性はありません。その理由は、UTF-16 においては BMP 文字 (タグの文字) で使用されない値のコード単位を使用して補助文字が表されるためです。

個々の文字を独自に解釈するアプリケーション、個々の文字を Java プラットフォーム API に渡すアプリケーション、個々の文字を返すメソッドを呼び出すアプリケーションのうち、補助文字を取り扱う可能性のあるアプリケーションの場合に限り、補助文字をサポートするための変更をアプリケーションに加える必要があります。char シーケンスを取り扱う類似 API を使用できる場合は、そのような API を使用するようにアプリケーションを変更するのが最善策です。それ以外の場合は、新しい API を使用して、char 型による表現をコードポイントによる表現に変換した後、コードポイント用 API を呼び出す必要があります。なお、上記の「書式化 API」で示したように、J2SE 5.0 には、補助文字をサポートすると同時にコードを簡素化する新しい便利な API が用意されています。

すべてのテキストをコードポイント表現 (たとえば int[]) に変換して処理するほうがよいのか、あるいは、可能な限り char シーケンスを維持して、必要な場合だけコードポイント表現に変換するほうがよいのか、迷う場合があると思われます。一般的に、Java プラットフォーム API では char シーケンスの使用が好ましい方法であり、char シーケンスを使用するほうがメモリ領域も節約されることに留意してください。

UTF-8 との間で変換を実行する必要があるアプリケーションについては、標準 UTF-8 または Modified UTF-8 のどちらを使用すべきのかを注意深く確認し、それぞれに適した Java プラットフォームユーティリティを使用しなければなりません。適正な UTF-8 の選択方法については、「Modified UTF-8」を参照してください。

補助文字を取り扱うユーザアプリケーションのテスト

前のセクションの説明に従ってアプリケーションを変更する場合でも変更しない場合でも、補助文字の取り扱いに関してアプリケーションが正常に動作するかどうかをテストすることをお勧めします。グラフィカルユーザインタフェース (GUI) を使用しないアプリケーションについては、「ソースファイル内での補助文字の表現」の説明を参考にしてテストを実施してください。ここでは、GUI を使用するアプリケーションのテストに関する補足情報を示します。

テキストの入力に関しては、Java 2 SDK に コードポイント形式でのインプットメソッドが用意されています。このインプットメソッドでは、"\Uxxxxxx" という形式の文字列を入力できます。大文字 "U" は、エスケープシーケンスに 6 個の 16 進数が含まれる (補助文字がサポートされる) ことを示します。一方、小文字 "u" は、元来の形式のエスケープシーケンス "\uxxxx" を示します。この入力方式とそのドキュメントは、J2SDK の demo/jfc/CodePointIM ディレクトリにあります。

フォントを描画するには、必要な補助文字を描画できるフォントを用意する必要があります。このようなフォントの例として、James Kass の Code2001 フォントがあります。このフォントには、Deseret や Old Italic などのスクリプト用のグリフが含まれています。Java 2D ライブラリの新機能により、J2RE の lib/fonts/fallback ディレクトリにフォントをインストールすれば、そのフォントが 2D と XAWT の描画に使用される各論理フォントに自動的に追加されます。ユーザがフォント構成ファイルを編集する必要はありません。

上記の確認作業が完了すれば、アプリケーション内で補助文字を問題なく使用できると言ってよいでしょう。おめでとうございます!

まとめ

補助文字をサポートする機能が Java プラットフォームに組み込まれました。補助文字のサポートにおいては、ソースコードを変更しなくても、ほとんどのアプリケーションで補助文字を取り扱えるようにするアプローチが採用されています。個々の補助文字を操作/解釈するアプリケーションでは、Character クラス内およびさまざまな CharSequence サブクラス内の新しいコードポイント用 API を使用できます。

謝辞

Java プラットフォームにおける補助文字のサポートは、Java Community Process(JCP)の内部組織である JSR-204 専門家グループによって設計開発されました。当専門家グループの仕様リードは、Sun Microsystems の奥津 正義と Brian Beck です。JSR-204 専門家グループのメンバーは、次のとおりです(敬称略、アルファベット順) : Craig Cummings(Oracle)、Mark Davis (IBM)、Markus Eble (SAP AG)、Jere Käpyaho (Nokia Corp.)、風間 一洋 (NTT)、数村 憲治 (富士通)、木村 英一(日本電気)、Changshin Lee (Tmax Soft Inc.)、村田 稔樹 (沖電気工業)
リファレンス実装は、IBM Globalization Center of Competency, San José の協力のもと、Sun Microsystems の Java 国際化チームによって行われました。仕様に関するテクノロジ互換性キットは、Sun Microsystems の JCK チームが実装した Java Compatibility Kit です。

リファレンス

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

JavaTM 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.

著者について

Norbert Lindenberg は、Sun Microsystems の Java Web Services グループにおけるJava 国際化のテクニカル・リードです。Sun に入社する前は、General Magic と Apple Computer において、さまざまな国際化プロジェクトに参画していました。彼は、ドイツのカールスルーヘ大学でコンピュータサイエンスの修士号を修めています。

奥津 正義は、Sun Microsystems の Java Web Services グループの国際化エンジニアで、現在 Java Specification Request 204, Unicode Supplementary Character Support の仕様リードを務めています。Sun に入社する前は、Digital Equipment Corporation において、さまざまな国際化プロジェクトに参画していました。彼は、日本の山形大学で電子工学の学士号を修めています。



1この Web サイトで使用している "Java Virtual Machine" または "JVM" という用語は、Java プラットフォーム用の仮想マシンを意味します。

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.