背景
我已经使用 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"
我正在尝试做的事情
我想在使用输入时从日志文件中积累一些统计信息。例如,我正在解析响应代码,我想计算有多少 2** 响应和多少 4/5** 响应。我正在解析作为 Ints 返回的每个响应的字节数,并且我想有效地将这些相加(听起来像foldl'
?)。我已经定义了这样的数据类型:
data Stats = Stats {
successfulRequestsPerMinute :: Int
, failingRequestsPerMinute :: Int
, meanResponseTime :: Int
, megabytesPerMinute :: Int
} deriving Show
我想在解析输入时不断更新它。但是在我消费时执行操作的部分是我卡住的地方。到目前为止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
期望的结果
我想将每个解析的行传递给将更新上述数据类型的函数。理想情况下,我希望这非常节省内存,因为它将在大文件上运行。