「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はもう古いのでは?」と訊かれると返答に困ります。
Pythonもダメでしたー。
ちなみに、下が長い吉をMacのEmacsに貼り付けるとコードに展開されます。これはこれで、シュール。
それはご愁傷様です。
でも、PythonはJavaと違って、
後方互換性を無視したアップデートにあまり抵抗がなさそうなので、
そのうち大丈夫になるかもしれませんよ。