Gar*_*rns 5 parsing haskell attoparsec
我使用attoparsec编写了一个日志文件解析器.我所有较小的解析器都是成功的,组合的最终解析器也是如此.我通过测试证实了这一点.但是我对使用解析后的流执行操作感到磕磕绊绊.
我开始尝试将成功解析的输入传递给函数.但似乎得到的是Done (),我认为这意味着日志文件已经被这一点消耗了.
prepareStats :: Result Log -> IO ()
prepareStats r =
case r of
Fail _ _ _ -> putStrLn $ "Parsing failed"
Done _ parsedLog -> putStrLn "Success" -- This now has a [LogEntry] array. Do something with it.
main :: IO ()
main = do
[f] <- getArgs
logFile <- B.readFile (f :: FilePath)
let results = parseOnly parseLog logFile
putStrLn "TBC"
Run Code Online (Sandbox Code Playgroud)
我想在消耗输入时从日志文件中累积一些统计信息.例如,我正在解析响应代码,我想计算有多少2**响应和多少4/5**响应.我正在解析每个响应作为Ints返回的字节数,我想有效地对它们求和(听起来像foldl'?).我已经定义了这样的数据类型:
data Stats = Stats {
successfulRequestsPerMinute :: Int
, failingRequestsPerMinute :: Int
, meanResponseTime :: Int
, megabytesPerMinute :: Int
} deriving Show
Run Code Online (Sandbox Code Playgroud)
当我解析输入时,我想不断更新它.但是我消耗的操作部分是我遇到困难的地方.到目前为止print,我唯一成功传递输出的函数,它显示解析Done在打印输出之前返回成功.
我的主要解析器看起来像这样:
parseLogEntry :: Parser LogEntry
parseLogEntry = do
ip <- logItem
_ <- char ' '
logName <- logItem
_ <- char ' '
user <- logItem
_ <- char ' '
time <- datetimeLogItem
_ <- char ' '
firstLogLine <- quotedLogItem
_ <- char ' '
finalRequestStatus <- intLogItem
_ <- char ' '
responseSizeB <- intLogItem
_ <- char ' '
timeToResponse <- intLogItem
return $ LogEntry ip logName user time firstLogLine finalRequestStatus responseSizeB timeToResponse
type Log = [LogEntry]
parseLog :: Parser Log
parseLog = many $ parseLogEntry <* endOfLine
Run Code Online (Sandbox Code Playgroud)
我想将每个已解析的行传递给将更新上述数据类型的函数.理想情况下,我希望这是非常高效的内存,因为它将在大文件上运行.
如果每个日志条目恰好是一行,则有一个更简单的解决方案:
do loglines <- fmap BS.lines $ BS.readfile "input-file.log"
foldl' go initialStats loglines
where
go stats logline =
case parseOnly yourParser logline of
Left e -> error $ "oops: " ++ e
Right r -> let stats' = ... combine r with stats ...
in stats'
Run Code Online (Sandbox Code Playgroud)
基本上,您只是逐行读取文件并调用parseOnly每一行并累积结果。