Javaのサロゲートペアについて
前書き
暇だったので、Java Cookbook, 4th Editionを読んでたのですが、下記のような記述がありました。(適当翻訳)
Javaでサロゲートペアを扱う場合は、String.codePointAt()が使えるよ。
for (int i = 0 ; i < someString.length(); i++) { System.out.println(someString.codePointAt(i)); System.out.println(someString.charAt(i)); }
for文の中で、i++していることに違和感を感じました。
サロゲートペアとは
下記はわかりやすいよう、すごくざっくり + 適当な説明です。
パソコンにおいて、文字は数字で管理されています。
画面に表示するときに、特的のルールに従って、数字を文字に変換しているのです。
(0だったらAを表示みたいなイメージです。)
数字から文字に変換するルールはいっぱいあります。
JavaにおいてはUnicodeというルールが使われています。
Unicodeは、全ての文字を表現できるようにしようとしました。
しかし当初用意していた数値の範囲が、全ての文字を表すのには不足していました。
そこで2つの数字の組みで、一つの文字を表すことを考えました。
これがサロゲートペアです。
何に違和感を感じたのか?
サロゲートペアは2つの数字で、1つの文字を表します。
なのでサロゲートペアを含んでいる場合、i + 2にしないといけないはずです。
for (int i = 0 ; i < someString.length(); i++) { System.out.println(someString.codePointAt(i)); System.out.println(someString.charAt(i)); }
試してみる
下記のコードを試してみました。
String a = "𠀋" System.out.println(a.charAt(0)); // 該当する文字がないので'?'が表示される。 System.out.println(a.charAt(1)); // 該当する文字がないので'?'が表示される。 System.out.println(a.codePointAt(0)) // 131083が表示される。 System.out.println(a.codePointAt(1)) // 56331が表示される。
ここでコードポイントを取得したい場合、a.codePointAt(0)が適切です。 なので下記の例西違うと、期待と違った値を取得してしまいます。
for (int i = 0 ; i < someString.length(); i++) { System.out.println(someString.codePointAt(i)); System.out.println(someString.charAt(i)); }
正しいコード
下記が正しいと思います。
for (int i = 0; i < a.length(); ) { int tmp = a.codePointAt(i); System.out.println(tmp); i += Character.charCount(tmp); }
ポイントはCharacter.charCount()を使用しているところです。
charCount()はサロゲートペアを引数にした場合、2を返してくれます。
結論
サロゲートペアを使う場合は、ちょっと違うAPIを使わないといけないですね!