仕事で地震動波形のファイルを扱うことがある。地震動波形ってのは、地震の加速度を数値化したもので、こんなふうなファイルになっている。
Example wave 1 NUMBER OF DATA=12001, DT=0.010, MAX.=-779.834268(TIME= 22.520), FORMAT=10F8.2 -0.05 -0.05 -0.05 -0.05 -0.06 -0.06 -0.06 -0.06 -0.06 -0.07 -0.07 -0.07 -0.07 -0.07 -0.08 -0.08 -0.08 -0.09 -0.09 -0.09 -0.10 -0.10 -0.10 -0.11 -0.11 -0.12 -0.13 -0.13 -0.14 -0.15 -0.16 -0.17 -0.18 -0.20 -0.22 -0.24 -0.27 -0.29 -0.33 -0.37 -0.41 -0.45 -0.50 -0.56 -0.61 -0.68 -0.74 -0.82 -0.89 -0.97 -1.06 -1.15 -1.25 -1.35 -1.45 -1.56 -1.68 -1.79 -1.92 -2.05 -2.18 -2.32 -2.46 -2.61 -2.76 -2.91 -3.07 -3.23 -3.39 -3.55 -3.72 -3.89 -4.06 -4.24 -4.42 -4.60 -4.78 -4.96 -5.14 -5.31 (以下略)
見てのとおりヘッダが2行あって、3行目以降に8桁の固定長データが続いている。
今回はこれを Text.Parsec
を使ってパースしてみようってわけ。といっても最初はことを単純にするため、ヘッダを省略してデータの部分だけでやってみる。
-0.05 -0.05 -0.05 -0.05 -0.06 -0.06 -0.06 -0.06 -0.06 -0.07 -0.07 -0.07 -0.07 -0.07 -0.08 -0.08 -0.08 -0.09 -0.09 -0.09 -0.10 -0.10 -0.10 -0.11 -0.11 -0.12 -0.13 -0.13 -0.14 -0.15 -0.16 -0.17 -0.18 -0.20 -0.22 -0.24 -0.27 -0.29 -0.33 -0.37 -0.41 -0.45 -0.50 -0.56 -0.61 -0.68 -0.74 -0.82 -0.89 -0.97 -1.06 -1.15 -1.25 -1.35 -1.45 -1.56 -1.68 -1.79 -1.92 -2.05 -2.18 -2.32 -2.46 -2.61 -2.76 -2.91 -3.07 -3.23 -3.39 -3.55 -3.72 -3.89 -4.06 -4.24 -4.42 -4.60 -4.78 -4.96 -5.14 -5.31 (以下略)
Haskell のパーサコンビネータ Parsec には Parsec 2 相当の Text.ParserCombinators.Parsec
と Parsec 3 相当の Text.Parsec
がある。最近では Parsec 2 ではなく Parsec 3 (つまり Text.Parsec
)を使え、ということのようなんだけど、Web で検索しても Text.ParserCombinators.Parsec
ばかりで、結構苦労した。特にパーサの型がわからなかった。結局、各パーサの型を書くのをやめて、パースを実行する関数にだけ型を書いたら動いてくれた。書いたコードがこれ。
module Main where import System.Environment ( getArgs ) import Text.Parsec -------------------------------------------------------------------------------- main :: IO () main = do args <- getArgs cs <- readFile (head args) let wv = parseWave cs either (\ e -> print e) (\ w -> putStr $ unlines $ map show w) wv -------------------------------------------------------------------------------- -- Parsers wave = do ls <- many1 waveLine eof return $ concat ls waveLine = do ws <- many1 waveData newline return ws waveData = do d <- count 8 dataChar return $ read d dataChar = digit <|> oneOf "-. " -------------------------------------------------------------------------------- parseWave :: String -> Either ParseError [Double] parseWave input = parse wave "(unknown)" input --------------------------------------------------------------------------------
実行例:
takatoh@apostrophe $ runhaskell parsewave.hs data0.dat > result.txt takatoh@apostrophe $ head result.txt -5.0e-2 -5.0e-2 -5.0e-2 -5.0e-2 -6.0e-2 -6.0e-2 -6.0e-2 -6.0e-2 -6.0e-2 -7.0e-2
なんとか、うまく動いてくれた。
ちなみに、ヘッダのついた data1.dat を食わせるとこんなエラーになる。
takatoh@apostrophe $ runhaskell parsewave.hs data1.dat "(unknown)" (line 1, column 1): unexpected "E" expecting digit
「Text.Parsecで固定長データをパースする」への2件のフィードバック