Javaプログラマは文字列の操作方法を確認した方がいいかもしれない

「Twitter時代の文字の数え方」という記事で書いたことですが、「文字列を扱う」というよくあるタスクは、プログラミング言語によって扱いやすさがまったく異なります。Javaには不幸な呪いがかかっていて、そのうち、「文字列操作に関しては、例えば、Javaを避ける」と言われるようになるのではないかと心配しています。

「文字列中の文字を、1行に1文字ずつ表示する」という例題を考えましょう。

注意:話を単純にするために、Unicodeのコードポイント1つ分のことを「1文字」と呼んでいます。複数のコードポイントで文字を構成することもあるので、ほんとうはもっと複雑です。

次のような解答に、文句をつけられるものでしょうか。

String str="文字列";
int size = str.length();
for (int i = 0; i < size; i++) {
  System.out.println(str.charAt(i));
}

たいていの場合ちゃんと動きますが、文字列が「\uD842\uDFB7\u91CE\u5BB6」(𠮷野家)だった時にうまく動きません。(注意:「𠮷野家」は単なる例で、実際には「吉野家」と書くべきです。)

?
?
野
家

Javaの内部文字コードであるUTF-16では、「𠮷」は「\uD842\uDFB7」という2コード単位で表されます。先のコードは、間違ってこれを「\uD842」と「\uDFB7」の2文字として処理してしまうのです。

次のように書けば正しく動きます。

for (int i = 0; i < size; i++) {
  int codeunit = str.codePointAt(i);
  if (0xffff < codeunit) {
    System.out.println(str.substring(i, i + 2));
    i++;
  } else {
    System.out.println(str.charAt(i));
  }
}
𠮷
野
家

変数codeunitの値が0xffffより大きい場合には、次のコード単位とあわせて1文字(サロゲートペア)として処理しなければなりません。

Javaを長く使っている人にとっては何でもないことかもしれませんが、これからプログラミングを学ぼうとする人が、最初に挙げたようなコードではダメだと知ったら、きっとがっかりすると思います。最初のようなコードでもちゃんと動くプログラミング言語を持ってきて、「Javaはもう古いのでは?」と訊かれると返答に困ります。

「Javaでサニタイズするときの注意—Apache Commons Lang 2.xと3.0の場合」につづく

2 thoughts on “Javaプログラマは文字列の操作方法を確認した方がいいかもしれない

  1. Pythonもダメでしたー。
    ちなみに、下が長い吉をMacのEmacsに貼り付けるとコードに展開されます。これはこれで、シュール。

    • それはご愁傷様です。

      でも、PythonはJavaと違って、
      後方互換性を無視したアップデートにあまり抵抗がなさそうなので、
      そのうち大丈夫になるかもしれませんよ。

コメントは停止中です。