はじめに
少し前から LTSV が話題になっています.ログ出力データの処理がやりやすそうです.
上記サイトに載っているとおり仕様がシンプルですから,パーサの練習問題としてもってこいな気がしました.
attoparsec による実装
ByteString
の扱いがうまくできていないような気もしますが,おそらく期待する動作になるはずです.
{-# LANGUAGE OverloadedStrings #-} import Data.ByteString (ByteString, pack) import Data.Attoparsec.ByteString import Data.Word (Word8) import Control.Applicative ( (<*), (<|>) ) type Field = (ByteString, ByteString) type Record = [Field] type LTSV = [Record] tab, cr, lf, colon :: Parser Word8 tab = word8 9 cr = word8 13 lf = word8 10 colon = word8 58 -- | -- LTSV format parser. -- -- >>> parseOnly ltsv "aaa:111\tbbb:222\nccc:333\tddd:444" -- Right [[("aaa","111"),("bbb","222")],[("ccc","333"),("ddd","444")]] -- ltsv :: Parser LTSV ltsv = do rs <- many' recordNL r <- record return $ if null r -- 0 or 1 を表す方法は...? then rs else rs ++ [r] recordNL :: Parser Record recordNL = record <* nl where nl = (cr >> lf) <|> lf record :: Parser Record record = sepBy field tab where field = do l <- label <* colon v <- value return (pack l, pack v) label = many1 $ satisfy $ inClass "0-9A-Za-z_.-" value = many' $ satisfy $ \w -> w `notElem` [9, 10, 13]
attoparsec-conduit パッケージと組み合わせることで,ストリーミングデータに適用可能となります.
(上記コードの recordNL
を sinkParser
に渡す感じ)