タイトル通りですが、Java内部の文字コードはUTF-16です。
Java関連の記事や書籍で時々見かけていたこの事実ですが、正直それで一体なんなの?という感じでした。
ファイルを読み込みしたり、書き込みしたりする場合に意識しておけばだいたいOKかなという程度の理解で、今までJavaを使っていて特に問題になることはありませんでした。
でも、そういえばUTF-16はサロゲートペアの問題あるよな。。。と思い軽く調べました。
UTF-16のサロゲートペアとは?
UTF-16は数ある文字コードの1つで、2バイトで文字を表します。しかし、2バイトだと「65535通り」(2の16乗)しか表せないので、実はこれだけでは世界中の文字を表すには十分ではありません。
そのため、表せない文字は既存の2バイトの領域以外の部分を使用して、合計4バイトで表そうみたいな動きになり、その仕組みが「サロゲートペア」ということらしいです(参考の方々の記事がとてもわかりやすかったので、詳細はそちらをどうぞ)。
Javaの文字列クラスStringでサロゲートペアの文字の長さを確認する
ということで、簡単にUTF-16のサロゲートペアの話をしました。
そこで、Javaの文字列クラスStringでサロゲートペアの文字の長さはいったいどのように判定されるのか確認してみたいと思います。java.lang.Stringクラスのlength()メソッドでいくつか確認したいと思います。
まずは、アルファベット英語の文字の長さを図ります。「aiueo」はどうなるのか。
public class Sample { public static void main(String[] args) { String str = "aiueo"; System.out.println(str + "のlengthは、" + str.length()); } }
はい、こちらはString.length()の結果は、もちろん5になりました。別に何も違和感はありません。
次は、日本語のひらがなの長さを図ります。「あいうえお」はどうなるのか。
public class Sample { public static void main(String[] args) { String str = "あいうえお"; System.out.println(str + "のlengthは、" + str.length()); } }
はい、こちらもString.length()の結果は、5になりました。いい感じですね。
それでは、やっとサロゲートペアの文字を試してみます。サロゲートペア文字は、「𠀋」で試してみます。「𠀋𠀋𠀋𠀋𠀋」はどうなるのか。
public class Sample { public static void main(String[] args) { String str = "𠀋𠀋𠀋𠀋𠀋"; System.out.println(str + "のlengthは、" + str.length()); } }
なんと!String.length()の結果が、10になってしまいました。5ではない。。何かしら文字数で処理する実装があった場合、今回のようなサロゲートペアの文字がある場合は障害になってしまうかもしれません。正直知らなかったので驚きました。
java.lang.StringのlengthメソッドのJavaDocを見てみる
StringのlengthメソッドをJavaDocで見てみると以下のように記載されています。
String (Java Platform SE 8)
「長さは文字列内のUnicodeコード単位の数に等しくなります。」という記載があります。
今回検証したサロゲートペア文字「𠀋」は、UTF-16でエンコーディングすると「D840 DC0B」(16進数)になります。
おそらく、Unicodeコード単位というと「D840」(2Byte)で1文字判定となっているはずなので、今回の文字は1文字(𠀋)だけど、長さが2として判定されるようになっていると思われます。