ものがたり(旧)

atsushieno.hatenablog.com に続く

マイナー言語を無視するMS.NETのCompareInfo

今日はCompareInfoでCompareOptions.Noneを指定していても無視される文字について。

僕が自分の環境で調べた限りでは、MS.NETのCompareInfoは、以下のカテゴリに属する文字列をまともに比較せず、常に空文字列と同様に扱います:

あと

も無視されます。(CompareOptions.Ordinalを使ってUnicodeコードポイントで比較すれば無視されない)。

↓のコード(昨日予告で出したやつ)はチェロキー語のwikipedia http://chr.wikipedia.org/wiki/ から引っ張ってきた文字列なんですが、実行すると0を出力します…少なくともうちの環境では。*1


CompareInfo ci = CultureInfo.InvariantCulture.CompareInfo;
string s1 = "\u13C5\u13B5\u13AE\u13B5\u13CD\u13D7";
string s2 = "\u13A0\u13C2\u13D0\u13A2";
Console.WriteLine (ci.Compare (s1, s2));

これらはInvariantCultureで判定しました。

これらの文字列が入力として入ってきて困る場合は、CompareOptions.Ordinalを常に使用しなければならないわけです(InvariantCultureを使っても解決しません)。ま、ぶっちゃけ、そんなコードが常に書けるとはとても思えません。これはculture dependentな*2文字列操作をデフォルトに設定したMicrosoftの設計上の欠陥ではないでしょうか。.NETは、Cで言えばsetlocale(LC_ALL, "")を設定してもダメ、みたいな状況です。

以下に、簡単にやっつけた「無視される文字判定」コードを貼っつけておきます。例の\u3007(例の〇文字)も含まれています。


public static bool IsIgnorable (int i)
{
switch (i) {
case 0:
// No idea why each of those is ignored.
case 0x2df: case 0x387:
case 0x3d7: case 0x3d8: case 0x3d9:
case 0x3f3: case 0x3f4: case 0x3f5: case 0x3f6:
case 0x400: case 0x40d: case 0x450: case 0x45d:
case 0x587: case 0x58a: case 0x5c4: case 0x640:
case 0x653: case 0x654: case 0x655: case 0x66d:
case 0xb56:
case 0x1e9b: case 0x202f: case 0x20ad:
case 0x20ae: case 0x20af:
case 0x20e2: case 0x20e3:
case 0x2139: case 0x213a: case 0x2183:
case 0x2425: case 0x2426: case 0x2619:
case 0x2670: case 0x2671: case 0x3007:
case 0x3190: case 0x3191:
case 0xfffc: case 0xfffd:
return true;
// exceptional characters filtered by the
// following conditions (no idea why though).
case 0x4d8: case 0x4d9: case 0x4e8: case 0x4e9:
case 0x70f: case 0x3036: case 0x303f:
case 0x337b: case 0xfb1e:
return false;
}

if (
// The whole Sinhala characters.
0x0D82 <= i && i <= 0x0DF4
// The whole Tibetan characters.
|| 0x0F00 <= i && i <= 0x0FD1
// The whole Myanmar characters.
|| 0x1000 <= i && i <= 0x1059
// The whole Etiopic, Cherokee,
// Canadian Syllablic, Ogham, Runic,
// Tagalog, Hanunoo, Philippine,
// Buhid, Tagbanwa, Khmer and Mongorian
// characters.
|| 0x1200 <= i && i <= 0x1DFF
// Greek extension characters.
|| 0x1F00 <= i && i <= 0x1FFF
// The whole Braille characters.
|| 0x2800 <= i && i <= 0x28FF
// CJK radical characters.
|| 0x2E80 <= i && i <= 0x2EF3
// Kangxi radical characters.
|| 0x2F00 <= i && i <= 0x2FD5
// Ideographic description characters.
|| 0x2FF0 <= i && i <= 0x2FFB
// Bopomofo letter and final
|| 0x31A0 <= i && i <= 0x31B7
// White square with quadrant characters.
|| 0x25F0 <= i && i <= 0x25F7
// Ideographic telegraph symbols.
|| 0x32C0 <= i && i <= 0x32CB
|| 0x3358 <= i && i <= 0x3370
|| 0x33E0 <= i && i <= 0x33FF
// The whole YI characters.
|| 0xA000 <= i && i <= 0xA48C
|| 0xA490 <= i && i <= 0xA4C6
// American small ligatures
|| 0xFB13 <= i && i <= 0xFB17
// hebrew, arabic, variation selector.
|| 0xFB1D <= i && i <= 0xFE2F
// Arabic ligatures.
|| 0xFEF5 <= i && i <= 0xFEFC
// FIXME: why are they excluded?
|| 0x01F6 <= i && i <= 0x01F9
|| 0x0218 <= i && i <= 0x0233
|| 0x02A9 <= i && i <= 0x02AD
|| 0x02EA <= i && i <= 0x02EE
|| 0x0349 <= i && i <= 0x036F
|| 0x0488 <= i && i <= 0x048F
|| 0x04D0 <= i && i <= 0x04FF
|| 0x0500 <= i && i <= 0x050F // actually it matters only for 2.0
|| 0x06D6 <= i && i <= 0x06ED
|| 0x06FA <= i && i <= 0x06FE
|| 0x2048 <= i && i <= 0x204D
|| 0x20e4 <= i && i <= 0x20ea
|| 0x213C <= i && i <= 0x214B
|| 0x21EB <= i && i <= 0x21FF
|| 0x22F2 <= i && i <= 0x22FF
|| 0x237B <= i && i <= 0x239A
|| 0x239B <= i && i <= 0x23CF
|| 0x24EB <= i && i <= 0x24FF
|| 0x2596 <= i && i <= 0x259F
|| 0x25F8 <= i && i <= 0x25FF
|| 0x2672 <= i && i <= 0x2689
|| 0x2768 <= i && i <= 0x2775
|| 0x27d0 <= i && i <= 0x27ff
|| 0x2900 <= i && i <= 0x2aff
|| 0x3033 <= i && i <= 0x303F
|| 0x31F0 <= i && i <= 0x31FF
|| 0x3250 <= i && i <= 0x325F
|| 0x32B1 <= i && i <= 0x32BF
|| 0x3371 <= i && i <= 0x337B
|| 0xFA30 <= i && i <= 0xFA6A
)
return true;

UnicodeCategory uc = Char.GetUnicodeCategory ((char) i);
switch (uc) {
// ignored by nature
case UnicodeCategory.PrivateUse:
case UnicodeCategory.Surrogate:
return false;
case UnicodeCategory.Format:
case UnicodeCategory.OtherNotAssigned:
return true;
default:
return false;
}
}

これは文章をひねり直してからmonologueの方に載せようかな。

*1:もしこれが違う環境では違う出力になるのだとしたら、InvariantCultureの存在意義が疑われます。

*2:より誤解を招かない言い方で書けば、「InvariantCultureというcultureに依存した」