Haskellにはバイナリを読み書きする時に使えるBitSyntaxというライブラリがあります。rubyで言うところのpackとかunpackに近いでしょうか。パースする時には返り値の型が引数に依存するので、その部分のコードはTemplate Haskellになっています。例えば、tzfile形式のファイルをパースするならこんな感じになります。
{-# LANGUAGE TemplateHaskell #-}
import Control.Exception
import Control.Monad
import Data.BitSyntax
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import Data.Maybe
import System.IO
parseFile :: FilePath -> IO ()
parseFile path = do
bracket (openFile path ReadMode)
hClose
(parse path)
parse :: String -> Handle -> IO ()
parse name h = do
header <- BS.hGet h 44
let Just (magic, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt) =
$(bitSyn ([Fixed 4, Skip 16] ++ replicate 6 (Unsigned 4))) header
when (magic /= BS8.pack "TZif") $
fail "Invalid magic."
transitionTimes <- forM [1..timecnt] $ \_ -> do
liftM decodeU32 $ BS.hGet h 4
localTimeTypes <- forM [1..timecnt] $ \_ -> do
liftM decodeU8 $ BS.hGet h 1
types <- forM [1..typecnt] $ \_ -> do
b <- BS.hGet h 6
return $ fromJust $ $(bitSyn [Unsigned 4, Unsigned 1, Unsigned 1]) b
abbrevs <- BS.hGet h (fromIntegral charcnt)
leaps <- forM [1..leapcnt] $ \_ -> do
b <- BS.hGet h 8
return $ fromJust $ $(bitSyn [Unsigned 4, Unsigned 4]) b
isstds <- forM [1..ttisstdcnt] $ \_ -> do
liftM decodeU8 $ BS.hGet h 1
isgmts <- forM [1..ttisgmtcnt] $ \_ -> do
liftM decodeU8 $ BS.hGet h 1
return ()