蝉は、やがて死ぬる午後に気づいた。ああ、私たち、もっと仕合せになってよかったのだ。:2015年06月20日分

2015/06/20(Sat)

[Windows][I18N] Windows 10 の国際化についての違和感 (その2)

前回の続き、Windows 10 のスタートメニューの国際化について検証していきたいと思います、今回も途中で飽きて放置しそうですが(ぉ

@日本語版のスタートメニューの違和感

再掲、「あ~ん(=かな)」でインデックス化されていますが、漢字のメニューはすべて「漢字」の項に追いやられています。 こんな国語辞典とかアドレス帳アプリが存在したら使い物になりませんですやね。

@Windows7までのスタートメニューの仕様であれば、とりあえず文字コード順に並べるだけでも見栄えはよかった

こちらも再掲ですが

と文字コードの昇順に列挙されてるだけです、いや違うこれ正しくは文字コード順ではないんですけどね…

よくみると「ネットワーク~」の次に「はじめに」がきて、その次は「ファイル名~」です。これが文字コード順だったら

  • は(U+306f)じめに
  • ネ(U+30cd)ットワーク~
  • フ(U+30d5)ァイル名~

の順番になるはずなのですが、ひらがなカタカナを無視して50音順になっています。

実はこれ I18N の世界には「文字列照合順序(Collation)」というものがありまして、それによって定められたルールに従って並び替えてるんですな。

@文字列照合順序とは?

文字列をソートする時、プログラミング幻語 C なら

$ cat >sortstr.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int
cmp(const void *p1, const void *p2)
{
	const char *s1, *s2;
	s1 = *((const char **)p1);
	s2 = *((const char **)p2);
	return strcmp(s1, s2);
}

int
main(int argc, char **argv)
{
	int i;

	qsort(&argv[1], argc - 1, sizeof(*argv), cmp);
	for (i = 1; i < argc; ++i)
		printf("%s\n", argv[i]);
}
^D

のような qsort(3)strcmp(3)を使ったコードを書くかと思います。

はいそこのアルゴリズム狂の方、今の話題においてソートの安定・不安定とか速度とかメモリ使用量とかどうでもいいので、そのチャックからボロンと取り出した TimSortとか BogoSortとか汚いものは速やかにズボンにおしまいになってご着席ください、座れっていってんだろ(怒)!

しかし困ったことにこのプログラムでは文字列の比較を strcmp(3)でやってるので、文字コード順でしかソートできません。

$ make sortstr
$ ./sortstr.exe ネットワーク はじめに ファイル名
./sortstr
はじめに
ネットワーク
ファイル名

さっきのスタートメニューとは異なり「ひらがな→カタカナ」の順になってしまっております。

@文字列照合順序に従ったソートロジックを書く

ある特定の言語地域において「自然な」文字列の大小関係を返すAPIとして、POSIX localeには strcoll(3)という関数があります。

ここで注意、*BSD では LC_COLLATE がまともに実装されておりませんので Linux とか Cygwin とか実装済の環境でテストしてくだちい。FreeBSD は 一応 collation サポート入ってるはずですが、multibyte な locale でも正しく動作するかは知りません。NetBSD と OpenBSD に関しては strcoll(3) は strcmp(3) とまったく同じ動作をするので期待した結果は得られません。

さっきの sortstr.c に以下の差分を適用します。

--- sortstr.c.orig	2015-06-20 05:11:37.582263100 +0900
+++ sortstr.c	2015-06-20 05:12:44.659099700 +0900
@@ -1,3 +1,4 @@
+#include <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -8,7 +9,7 @@
 	const char *s1, *s2;
 	s1 = *((const char **)p1);
 	s2 = *((const char **)p2);
-	return strcmp(s1, s2);
+	return strcoll(s1, s2);
 }
 
 int
@@ -16,6 +17,7 @@
 {
 	int i;
 
+	setlocale(LC_COLLATE, "");
 	qsort(&argv[1], argc - 1, sizeof(*argv), cmp);
 	for (i = 1; i < argc; ++i)
 		printf("%s\n", argv[i]);

そんでビルドして実行すると?

$ make sortstr
$ LC_COLLATE=ja_JP.UTF-8 ./sortstr.exe ネットワーク はじめに ファイル名
./sortstr
ネットワーク
はじめに
ファイル名

おお、ちゃんとひらがなとカタカナの違いを無視して50音順にソートされました!

ちなみに

strcoll(s1, s2);

は s1 と s2 を strxfrm(3)によって変換したもの同士で strcmp(3)した結果と等価になります、よって照合順序の肝はこの関数ですが、実装の中身については今回は扱わず、また後の回にて説明します。

@次回予告

今回の説明で Windows 7 までのスタートメニューの実装であれば 文字列照合順序(Collation)APIを使えば簡単に実装できることはお判りいただけたと思います。

次回は Windows 10 の新仕様の実装の難しさについてを

  • 日本語における文字列照合順序の工業規格
  • C/POSIX localeにおける実装の制限

について解説しつつ確認していきたいと思います。