ものがたり(旧)

atsushieno.hatenablog.com に続く

「MS ACCESS 95/97 の美しいソート順」の謎に答える

ええと、以前1度だけここでもリンクしたことがあるのですが、collationの分析まわりでは日本に偉大な先人がいたわけです。ここで説明されていることは、僕が知る限りLCMapStringの動作と矛盾しないので、おそらくAccessに限らずExcelなどでも同様のソーティングが行われるものと思いますが、LCMapStringの動作に深入りすることなくここまで調べ上げられた努力にはもう脱帽せざるを得ません。

さて、この文書の分析で、なお未解決の謎が残っているようなので、僕が勝手に補足してみます。ええ、もう大体のことは分かります。めっちゃ長いのでひさしぶりに「続く」を入れてみましょうか。


で、のっけからなんですが、AccessBasicのレベルで文字比較の推移律が成立しない理由は、僕には分かりません。もしかして、全角ダッシュがマイナス記号と見なされたりしていたりしますかね。だとするとsortkey値をsigned値で反転してそれをunsignedで比較して…とか、あり得なくは無いですが(人はそれをバグという^^;)。


半濁点 1個は(なぜだか知りませんが)濁点 2個ぶんと同じとみなします
濁点はnonspacingなもので、直前の文字のdiacritical weightを1増やします。半濁点も同様ですが、2増やします。なので、濁点が2つ付くと半濁点と同じ扱いになってしまうわけです。ちなみに同一の基底文字をもつ2つのdiacritical markが付いた文字のどちらかに濁点をいくつか付けていくと、いずれ同値になります。(w


合成用丸を無視するのはいいとして, 漢数字のゼロを無視するのは (いくら形が似ているからといって) 合点が行きません。
何度か取り上げましたが、少なくともUnicode 3.2以降の文字は全て無視されます。それ以前のバージョンの文字でも、1.1に含まれないものは、多くが無視されます。

次、ソート順の分析について。


両方の文字列がハイフン文字を含んでいるが, ハイフン文字が最初に出現する場所が違う場合, 後ろに出てきた方が大きいとみなす。
ハイフンおよびアポストロフィはspecial weightで比較されます(なので、CompareOptions.StringSortを指定した比較では、特別な重みは設定されず、他の文字よりかなり小さい重みを持つ文字と見なされます)。なぜ「後から出た方が大きい」のかというと、special weightのsortkeyのフォーマットがたまたま[offset char-weight]という構成になっているからです。offsetは先頭文字からのオフセットになっているので、これが大きければ大きいほど、文字列が「大きい」ことになります。

次、幾何学図形の重みについて。


11文字あります。やはり Unicode 順に並ぶのですが, 同じ形の図形(四角・三角・逆三角・ひし形・丸) のバリアントどうしは, Unicode の値の差だけ濁点をつけたものとみなします。 (例: ■゛=□, ◎゛=●, ○゛゛゛ =◎) なんでだー!!
これらの文字は、それぞれの形について、primary weightでは同一であり、diacritical weightでのみ違いが存在します。なので、CompareOptions.IgnoreNonSpaceを用いて比較すると、同じ文字であるとみなされます。さて、先ほど濁点と半濁点について説明した箇所を読み直してもらえれば、これらがdiacritical weightを加算するだけの文字であることが分かると思います。で、これらの図形はそれぞれdiacritical weightのみの違いとなるので、「どちらかに加算していけば…」と↑で書いたとおりのことが起こります。ちなみに■にはthirtiary weightもあるので、本当に□と同一と見られるとしたら、AccessではデフォルトでCompareOptions.IgnoreWidthおよびCompareOptions.IgnoreCaseを用いて比較しているのかもしれません(アルファベットの大文字小文字が区別されないと後で書かれているので)。

罫線記号について、


32文字あります。 Unicode 順に並びますが, 例によって罫線の太さの差は,濁音と同じように処理します。
実はこれは僕がCompareInfoで行った結果と合致しません。なぜなら、これらの文字はJIS X 0201にも含まれるので、日本語ロケールにおいては、JISのソート順(JIS X 4061)に基づいてソートされるためです。これはprimary weightの順番しか並び替えないので、diacritical weightでしか違いのないこれらの文字は、やはり幾何学図形と同じ結果になります。

一般記号について


下の 5文字は本来みな無効な文字だが, 上記「・」と同値だと評価する。 おそらく Access のバグと思われる。 (SJIS: 8540 8640 eb40 ec40 ef40)
これは原因が分かりませんが、おそらくSJIS&->Unicodeマッピングのバグであろうと推測します。

飾り文字


7文字あります。おおむね Unicode 順に並んでいますが, なぜか★と☆の順だけひっくり返っています。
JIS X 4061の順番であると推測します。理由は罫線記号について書いたのと同じです。


「℃」は C に半濁点のついた文字とみなします。
これは実は笑える単なる偶然で、U+2102, U+2103(この文字), U+212Dがdiacritical weightで3, 4, 5…と並んでいます。実はWindowsが内部でどのような計算を行っているか明らかではないのですが、半濁点は例によって直前の文字のdiacritical weightを2増やします。で、2増やすだけならまだCに半濁点を付けてもU+2103とは一致しないのですが、実は2というのはdiacritical weightとthirtiary weightにおける「デフォルト値」を表すものであり、0と同じ意味になります。しかしそうなると、半濁点が付いたものは値がないことと同じになってしまうので、値が2以下である場合には、値を2加算しているのではないかと思います。なので、たぶんCに濁点を付けたらU+2102と同じ値になるんじゃないかと思います(未確認)。


例外として, 丸付き漢字・括弧付き漢字は丸を取り除いた文字の次に並びます。 (例: 右 < 右大臣 < ㊨ < 宇; 株 < 株式 < ㈱ < 兜)
実はこれらにはさらに例外があって、㊘、㊢、㊫などは正しく並べてもらえません。これはちょっと前にここで書いた、CJK互換文字に関するCとJの混同が原因です。

長音と踊り字の解析は見事ですね。長音はUCAやCLDRでも考慮されているので気づく人も多いかもしれませんが。

そんなわけで


これで私の Access ソート順の巨大な仕様の解析を終わりますが, どなたかエミュレートするプログラムを作ってみませんか?
と提案されているわけですが、僕はこれを解析・実装するだけで数ヶ月かかってしまったので、あまりお薦めしません。(笑)