文字コードの復習をちょっとしてたら、Unicodeのサロゲートペアって今まで気にしたこと無かったことに気が付きました。
ということで、お仕事でも扱うJavaで、サロゲートペアの文字列を組み立てたり、文字数をカウントしたりする練習をしました。
Java 1.4まではサロゲートペアは考慮されてませんでしたが、1.5になり、サロゲートペアを考慮したAPIが追加されました。そこで、上記テストコードでは1.4系までのAPIと、1.5で追加されたAPIを呼び出してみて、挙動を比べてます。
まず char 型でサロゲートペアを表現してみます。上位サロゲート、下位サロゲートをそれぞれ別のchar型変数として分け、配列に組み込んでます。
char c1 = '\u3042'; // HIRAGANA LETTER A, cp=12354
char c2 = '\uD842'; // tuchi-yoshi (high), cp=134071
char c3 = '\uDFB7'; // tuchi-yoshi (low), cp=134071
char c4 = '\u30D5'; // katakana fu, cp=12501
char c5 = '\u309A'; // handakuten, cp=12442
char c6 = '\uD842'; // kuchi + shichi (high), cp=134047
char c7 = '\uDF9F'; // kuchi + shichi (low), cp=134047
String s = new String(new char[] { c1, c2, c3, c4, c5, c6, c7 });
assertEquals(s, "\u3042\uD842\uDFB7\u30D5\u309A\uD842\uDF9F");
続いてサロゲートペアを考慮しない String.length()
や String.charAt()
を使って文字列をコピーしてみます。最後の assertEquals()
を見ると、サロゲートペアが分割された状態の int[]
から生成した文字列と一致しています。上位サロゲート・下位サロゲートをそれぞれ独立した一文字として扱い、コピーしている様子が確認できます。
int len = s.length();
assertEquals(len, 7); // ignores surrogate pair :P
int[] actualCps = new int[len];
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
actualCps[i] = (int) c;
}
// Ignores surrogate pairs... :(
// BUT JavaScript unicode escape in browser accepts this format...:(
assertEquals(actualCps, new int[] { 0x3042, 0xD842, 0xDFB7, 0x30D5, 0x309A, 0xD842, 0xDF9F });
今度はサロゲートペアを考慮する String.codePointCount()
や String.codePointAt()
を使ってみます。最後の assertEquals()
を見ると、サロゲートペア対象文字をUnicodeコードポイントの16進数で表現した文字列と同じになります。サロゲートペアを1文字としてカウントし、扱えている様子が確認できます。
int countOfCp = s.codePointCount(0, len);
assertEquals(countOfCp, 5); // GOOD.
actualCps = new int[countOfCp];
for (int i = 0, j = 0, cp; i < len; i += Character.charCount(cp)) {
cp = s.codePointAt(i);
actualCps[j++] = cp;
}
// GOOD.
assertEquals(actualCps, new int[] { 0x3042, 0x20BB7, 0x30D5, 0x309A, 0x20B9F });
参考:
- Java による Unicode サロゲートプログラミング
- https://www.ibm.com/developerworks/jp/ysl/library/java/j-unicode_surrogate/
- これがJavaでのUnicodeの扱いや追加されたAPIについて非常に詳しい。実行速度への検討もされている。
- サロゲートペア入門:CodeZine
- JIS2004に含まれるサロゲートペア。Javaでの文字列操作を検証してみた | 株式会社メイプルシステムズ