2016年9月25日日曜日

日本語文字列の自然なソート

手元にあった海外製のJavaで書かれたソフトで日本語の文字列をソートすると、
漢字部分が無秩序、訳わからない並びになった。
Unicodeの並びに沿わせていないからであろうか?

※JavaはUnicodeの文字コードでUTF-16の符号化方式である。
 つまり、StringクラスはUTF-16形式で扱うわけである。
 char型は符号なし16ビット整数である。Characterクラスはこのラッパ。

【参考:一般的な文字コード(文字集合)と符号化方式】
 -文字コード-   -符号化(エンコード)方式-
 JIS X 0201   8bit符号
              7bit符号

 JIS X 0208   EUC-JP

              ISO-2022-JP
              Shift_JIS

 Unicode      UTF-16

              UTF-32
              UTF-8


しかし、ASCII部分、ひらがな、カタカナ、は問題なく、Unicodeに沿っているように見える。なぜ漢字にだけ違和感を感じるのだろうか。
少し調べたところ、Unicodeの漢字は部首順に並べられているようである。規則性を感じなかったのはそのためである。部首単位でまとまるためそれが統一性を感じることも逆にあるだろうが。

国や言語固有の文化を反映した自然だと思われる並びにするにはJavaであればjava.text.Collatorクラスを使うと解決することが分かった。日本語に限らず、他言語にも対応している。英語圏発のソフトウェアはこのあたりへ考え及ばせること難しいかもしれないが考慮入れていただきたい点の一つである。



(脱線1)
文字列比較をする際にはCollator#compare()メソッドを使えばいいが、繰り返しソートする場合は性能面からCollationKeyを利用したビット単位比較をさせた方がいい。

(脱線2)
Javaと文字列の話ついでに、もう一つ。
charの単位は1文字ではない。適切な区切りを得るためにはBreakIteratorクラスあたりを使うこと。

なぜかというと、UTF-16はBMP(Basic Multilingual Plane)以外の面の文字を表すためにサロゲートペア(surrogate pair)や、あと複数のパーツで1文字を表現する結合文字を存在しているからである。濁点、半濁点との結合などである。

(脱線3)
Rubyの1.8系までは少しおもしろい取り組みをしている。

JavaやPythonなどはシステムの内部コードをUnicodeの文字コードに統一するUCS(Universal Code Set)方式を取っている。そのためUnicodeに含まれない文字を扱いたければ、ライブラリを使うか、自力でchar単位で処理するしかない。Unicodeだけで問題あるのかというと、このセットに含まれない文字は実は結構あるらしい。

一方、Ruby1.8系までは8bitの列に符号化方式の情報をセットにしたものを文字列だと定義する、CSI(Code Set Independent)方式を採用している。文字コードはUnicodeではなく、ただのバイナリでしかない。どのような文字コードでも直接保存できるが、実装、最適化あたりの処理は複雑にならざるを得ないだろうと容易に想像できる。
ちなみに、Ruby1.9系以降のディフォルトのエンコーディング方式はUTF-8である。


〜参考〜
http://ruby-doc.org/core-2.3.1/String.html
http://magazine.rubyist.net/?0025-Ruby19_m17
http://d.hatena.ne.jp/nishiohirokazu/20141107/1415286729