一昨日の記事で、パーサの型も(一応)解った。なので、今度は Text.Parsecで固定長データをパースする で省いていたヘッダのパースをすることにする。さらに、パースしたデータをCSV形式のデータに変換する。
まずは、対象の地震動波形のファイルを再掲しておこう。
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行。1行目は地震動の名前、2行目はいくつかのパラメータで、ここで重要なのは「DT=0.010」の部分。DT は時刻刻み―つまりサンプリングレートで、0.01秒ごとにデータを記録していることを意味している。ほかのパラメータは今回は無視。
で、手始めに地震動を表すデータ型 Wave
を作った。
data Wave = Wave { wvName :: String
, wvDT :: Double
, wvData :: [Double]
} deriving Show
それから、ヘッダを含む地震動ファイル全体をパースする関数 waveFile
。
waveFile :: Parser Wave
waveFile = do
n <- waveName
dt <- waveDT
w <- wave
eof
return Wave { wvName = n, wvDT = dt, wvData = w }
waveFile
は Wave
を返すので、型は Parser Wave
だ。
あとは、コード全体を示そう。
module Main where
import System.Environment ( getArgs )
import Text.Parsec
import Text.Parsec.String
import Data.List
import Text.Printf
--------------------------------------------------------------------------------
data Wave = Wave { wvName :: String
, wvDT :: Double
, wvData :: [Double]
} deriving Show
--------------------------------------------------------------------------------
main :: IO ()
main = do
args <- getArgs
cs <- readFile (head args)
let wv = parseWave cs
either (\ e -> print e) (\ w -> putStr $ formatWave w) wv
--------------------------------------------------------------------------------
formatWave :: Wave -> String
formatWave w = unlines $ header : wv
where
header = "," ++ (wvName w)
wv = map (\ (t, d) -> printf "%.2f,%.2f" t d) $ zip tm (wvData w)
tm = 0.0 : unfoldr (\ x -> Just (x + dt, x + dt)) 0.0
dt = wvDT w
--------------------------------------------------------------------------------
-- Parsers
waveFile :: Parser Wave
waveFile = do
n <- waveName
dt <- waveDT
w <- wave
eof
return Wave { wvName = n, wvDT = dt, wvData = w }
line :: Parser String
line = do
l <- many1 (noneOf "\n")
newline
return l
waveName :: Parser String
waveName = line
waveDT :: Parser Double
waveDT = do
string "NUMBER OF DATA="
many1 digit
string ", DT="
dt <- many1 dataChar
many1 (noneOf "\n")
newline
return $ read dt
wave :: Parser [Double]
wave = do
ls <- many1
waveLine
return $ concat ls
waveLine :: Parser [Double]
waveLine = do
ws <- many1
waveData
newline
return ws
waveData :: Parser Double
waveData = do
d <- count 8 dataChar
return $ read d
dataChar :: Parser Char
dataChar = digit <|> oneOf "-. "
--------------------------------------------------------------------------------
parseWave :: String -> Either ParseError Wave
parseWave input = parse waveFile "(unknown)" input
--------------------------------------------------------------------------------
これで出来たはずだ。
実行例:
takatoh@apostrophe $ runhaskell parsewave4.hs data1.dat > result.csv
takatoh@apostrophe $ head result.csv
,Example wave 1
0.00,-0.05
0.01,-0.05
0.02,-0.05
0.03,-0.05
0.04,-0.06
0.05,-0.06
0.06,-0.06
0.07,-0.06
0.08,-0.06
OK。うまくいった。