【问题标题】:Parsing a log file in Haskell在 Haskell 中解析日志文件
【发布时间】:2017-03-09 18:15:40
【问题描述】:

我需要解析一个巨大的日志文件。我想在 Haskell 中做这件事是为了学习(我是初学者)。 日志文件的布局类似于:

parameter a_parameter_name errors: 5
error bla bla1
error bla bla2
error bla bla bla3
error bla bla bla4
error bla bla bla5
some garbage line
parameter an_other_parameter_name errors: 7
error bla bla1
error bla bla2
error bla bla3
error bla bla4
error bla bla5
error bla bla6
error bla bla7

some garbage line
some garbage line
some garbage line
...

此日志文件包含 2 种主要行类型:

  1. 以“参数”开头的行;
  2. 以“error”开头的行。

“错误”行与前一个参数行相关。 其他几行没什么意思。

我想做的是打印出参数及其错误,按错误数排序。所以在这里我想得到:

parameter an_other_parameter_name errors: 7
error bla bla1
error bla bla2
error bla bla3
error bla bla4
error bla bla5
error bla bla6
error bla bla7
parameter a_parameter_name errors: 5
error bla bla1
error bla bla2
error bla bla bla3
error bla bla bla4
error bla bla bla5

通过以下代码,我得到了有趣的行列表

import System.IO
import Data.List

interesting :: String -> Bool
interesting s = isPrefixOf "parameter" s || isPrefixOf "error" s

main = do
    logFile <- openFile "log.txt" ReadMode
    contents <- hGetContents logFile
    let interestingLines = filter interesting $ lines contents
    print interestingLines
    hClose logFile

从这个列表中,我想构建一个三元组列表: [(parameter, errorsNb, [errors])],我可以排序并打印出来。 但我不知道如何将错误行与其相关的参数行分组。但也许这不是正确的方法...... 欢迎任何帮助!

奥利维尔

【问题讨论】:

  • 有很多选择。您可以使用span 和显式递归来分割“错误”部分。或者您可能希望groupBy 将您的列表大致分组为[[param1],[err1,err2,err3],[param2],[err4],[param3]],然后再进行更多处理以根据需要对其进行排序。
  • 您应该考虑为此使用解析库,例如 Parsec 使这种工作变得轻而易举
  • 这与我在 Code Review 上的问题有一些相似之处,您可能需要检查there
  • 感谢大家的帮助!

标签: haskell


【解决方案1】:

我修改了CIS194(第 2 周)的解决方案。
转换为二叉树数据结构和懒惰读取文件将是很好的学习练习。

type Name = String
type Count = Int
data MessageType =  Param Name Count
                 | Error String
                 | Unknown String
                   deriving (Show, Eq)

parseMessage :: String -> MessageType
parseMessage line =
    case  words line of
      ("parameter":n:_:c:_) -> Param n (read c)
      ("error":msg)         -> Error (unwords msg)
      xs                    -> Unknown $ unwords xs

data LogMessage = LogMessage Name Count [MessageType]
               deriving (Show, Eq)

parse :: String -> [MessageType]
parse = map parseMessage .  lines

isError :: MessageType -> Bool
isError (Error _) = True
isError _ = False


isUnknown :: MessageType -> Bool
isUnknown  (Unknown _)  = True
isUnknown _ = False

(.||.) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
(.||.) f g a = (f a) || (g a)

toLogMsg :: [MessageType] -> [LogMessage]
toLogMsg [] = []
toLogMsg (x:xs) =
    case x of
      Param n c ->
          LogMessage n c (takeWhile isError xs) : toLogMsg (dropWhile (isError .||. isUnknown) xs)
      _         -> toLogMsg $ dropWhile (isError .||. isUnknown) xs



errMsgList :: [MessageType] -> [String]
errMsgList = foldr (\(Error m) acc -> m : acc) []

toTriple :: [LogMessage] -> [(String, Count, [String])]
toTriple = foldl(\acc (LogMessage n c xs) -> (n, c, errMsgList xs) : acc) []



main :: IO ()
main = do
       ts <- toLogMsg . parse <$> readFile "./src/2017/so-log.txt"
       mapM_ print ts
       mapM_ print (toTriple ts)

您提供的样本的输出将是:

("an_other_parameter_name",7,["bla bla1","bla bla2","bla bla3","bla bla4","bla bla5","bla bla6","bla bla7"])
("a_parameter_name",5,["bla bla1","bla bla2","bla bla bla3","bla bla bla4","bla bla bla5"])


LogTriple "a_parameter_name" 5 [Error "bla bla1",Error "bla bla2",Error "bla bla bla3",Error "bla bla bla4",Error "bla bla bla5"]
LogTriple "an_other_parameter_name" 7 [Error "bla bla1",Error "bla bla2",Error "bla bla3",Error "bla bla4",Error "bla bla5",Error "bla bla6",Error "bla bla7"]

【讨论】:

  • 我不知道CIS194,这看起来很有趣。感谢您的代码和链接!
  • 您能否将答案标记为有用或为该答案投票:)
猜你喜欢
  • 2016-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-30
相关资源
最近更新 更多