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;;;;;
…(略)…

HaskellのData.Timeを使ってみた

System.Timeは廃止予定でData.Timeを使えとのこと。

timeパッケージをインストール。
$ cabal install time

現在日時の取得と各種型変換とフォーマットのサンプル。ドキュメント見ながら試行錯誤でだらだら書いてみただけなので、もっと良いやり方と綺麗な書き方があると思いますが、とりあえずメモということで。
import Data.Time
import System.Locale

main = do
    t <- getCurrentTime
    putStrLn $ "day=" ++ (show $ toGregorian $ utctDay t)
    putStrLn $ "time=" ++ (show $ utctDayTime t)
    tz <- getTimeZone t
    putStrLn $ "timezone=" ++ show tz
    lzt <- utcToLocalZonedTime t
    let lt = zonedTimeToLocalTime lzt
        ltod = localTimeOfDay lt
    putStrLn $ "localTimeOfDay=" ++ show ltod
    putStrLn $ "localTimeOfDay hour=" ++ (show $ todHour ltod)
    putStrLn $ "localTimeOfDay min=" ++ (show $ todMin ltod)
    putStrLn $ "localTimeOfDay sec=" ++ (show $ todSec ltod)
    let fmt = iso8601DateFormat $ Just "%T"
    putStrLn $ "formattedUTCTime=" ++ (formatTime defaultTimeLocale fmt t)
    putStrLn $ "formattedLocalTime=" ++ (formatTime defaultTimeLocale fmt lzt)
実行結果
day=(2011,7,18)
time=58373.107045s
timezone=JST
localTimeOfDay=01:12:53.107045
localTimeOfDay hour=1
localTimeOfDay min=12
localTimeOfDay sec=53.107045000000
formattedUTCTime=2011-07-18T16:12:53
formattedLocalTime=2011-07-19T01:12:53

ブログ アーカイブ