使用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"

我想做什么

我要在消耗输入时从日志文件中累积一些统计信息。例如,我正在解析响应代码,我想计算有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

期望的结果

我想将每个解析的行传递给一个函数,该函数将更新上述数据类型。理想情况下,我希望它具有很高的内存效率,因为它将在大文件上运行。

埃里克

如果每个日志条目正好是一行,这是一个更简单的解决方案:

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'

基本上,您只是逐行读取文件并parseOnly在每一行上调用并累积结果。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章