To be a professional

プログラミング関係の情報

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を使わないといけないですね!

参考

qiita.com

www.ne.jp

www.ne.jp