Data.CharのGeneralCategoryにSurrogateという文字があることに気付いて対応しているのかなと思い、実験してみた。環境はScientific Linux 6.0+GHC7.0.3。
import Data.Char (GeneralCategory(..), generalCategory) main = do let c = '\x20213' putChar c putStrLn $ show c putStrLn $ show $ generalCategory c putStrLn $ if generalCategory c == OtherLetter then "yes" else "no" putStrLn $ if generalCategory c == Surrogate then "yes" else "no"
Mac OS X 10.6.8のターミナルからリモートログインして実行したときの結果。ターミナルではCourierフォントを使っていましたが文字化けせずに表示されました。
*Main> main 𠈓'\131603' OtherLetter yes no
予想外だったのはgeneralCategoryの結果がSurrogateではなくOtherLetterだったこと。調べてみるとこれは単に私がUnicodeをよく知らないだけでした(以下の調査結果参照)。
ghc-7.0.3-src.tar.bz2のlibraries/Base/Data/Char.hsを見てみたところ、以下のような定義になっていた。
-- | The Unicode general category of the character. generalCategory :: Char -> GeneralCategory #if defined(__GLASGOW_HASKELL__) || defined(__NHC__) generalCategory c = toEnum $ fromIntegral $ wgencat $ fromIntegral $ ord c #endif #ifdef __HUGS__ generalCategory c = toEnum (primUniGenCat c) #endifwgencatは79行目に定義されていました。
#ifdef __NHC__ import Prelude import Prelude(Char,String) import Char import Ix import NHC.FFI (CInt) foreign import ccall unsafe "WCsubst.h u_gencat" wgencat :: CInt -> CInt #endif
u_gencatはbase/cbits/WCsubst.cで定義されていました。
int u_gencat(int c) { return getrule(allchars,NUM_BLOCKS,c)->catnumber; }
getruleの定義はこちら。どうやらallcharsという配列をバイナリサーチで探すようだ。
static const struct _convrule_ *getrule( const struct _charblock_ *blocks, int numblocks, int unichar) { struct _charblock_ key={unichar,1,(void *)0}; struct _charblock_ *cb=bsearch(&key,blocks,numblocks,sizeof(key),blkcmp); if(cb==(void *)0) return &nullrule; return cb->rule; }
目で探してみるとこちら。
{131072, 42711, &rule45}, {194560, 542, &rule45},
rule45を見てみるとLO、つまりOtherLetterです。
static const struct _convrule_ rule45={GENCAT_LO, NUMCAT_LO, 0, 0, 0, 0};
一方、Surrogateを探すとこちらにありました。
static const struct _convrule_ rule157={GENCAT_CS, NUMCAT_CS, 0, 0, 0, 0};
使っている箇所は以下の3箇所でした。
{55296, 896, &rule157}, {56192, 128, &rule157}, {56320, 1024, &rule157}, {57344, 6400, &rule158},
http://www.unicode.org/Public/zipped/6.0.0/UCD.zipのUnicodeData.txtを見て謎が解決しました。General CategoryがCs (Surrogate)なのはサロゲートペアの片割れのコードということのようです。
…(略)… D800;;Cs;0;L;;;;;N;;;;; DB7F; ;Cs;0;L;;;;;N;;;;; DB80; ;Cs;0;L;;;;;N;;;;; DBFF; ;Cs;0;L;;;;;N;;;;; DC00; ;Cs;0;L;;;;;N;;;;; DFFF; ;Cs;0;L;;;;;N;;;;; …(略)… 20000; ;Lo;0;L;;;;;N;;;;; 2A6D6; ;Lo;0;L;;;;;N;;;;; …(略)…