length()の値が違う

エキサイト株式会社 メディアサービスエンジニアのしばたにえんです。
早速以下のコードを見てください

    void getStringLength() {
        String str = "𩸽";
        System.out.println(str.length());
    }

実はこの結果は2です。 原因はサロゲートペアにあります。

サロゲートペアとは

通常、Unicodeでは1文字あたり2バイトのデータ量を使います。 2バイトですから65536通り(0x0000~0xFFFF)のビットを表現できます。 この約6万字で世界中の文字を表現しようというのがUnicodeの本来の思想でした。 ところが、近年、Unicodeに組み込みたいという文字の要望がいろいろと増えてきました。 結果的に従来の2バイト(65536文字)では文字が足りない状況になってしまったのです。 そこで、解決策としてサロゲートペアという方法が導入されました。 「1文字=2バイト」の基本は維持しつつ、一部の文字については「1文字=4バイト」にする方法です。

    void getCharLength() {
        String str = "𩸽";
        System.out.println(Integer.toHexString(str.charAt(0)));
        System.out.println(Integer.toHexString(str.charAt(1)));
    }
// 実行結果
d867
de3d

"𩸽"は見た目上は1文字ですが、サロゲートペア文字で4バイトで表した文字でchar2つで表されます。length()はchar1つにつき1とカウントされるため値が違ったのです。

本当の文字数を取得する

    void getRealLength() {
        String str = "𩸽";
        System.out.println(str.codePointCount(0, str.length()));
    }
// 実行結果
1

codePointCountメソッドでは正しく1とカウントできていることが確認できます。 codePointCountメソッドでは、文字列をカウントする際の開始/終了位置が必須なので、文字列全体をカウントするには、それぞれ0(先頭)、str.length(末尾)を指定しています。

length()を使う際は気をつけましょう。