仕事で地震動波形のファイルを扱うことがある。地震動波形ってのは、地震の加速度を数値化したもので、こんなふうなファイルになっている。
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