hnakamur’s blog

ものすごい勢いで忘れる私のために未整理でもいいからとりあえずメモ

2011-07-19

GHCでサロゲートペアが使えるか試してみた

2 Lexical Structure6 Predefined Types and ClassesではCharはUnicodeの文字を表すことはわかるがサロゲートペアについては言及無し。

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)
#endif
wgencatは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;;;;;
…(略)…

0 件のコメント:

ブログ アーカイブ