2017-03-09 113 views
0

我需要解析一个巨大的日志文件。我想在Haskell中学习目的(我是初学者)。 日志文件的布局是这样的:解析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 

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

奥利弗

+1

有多种选择。你可以使用'span'和显式递归来分割“错误”部分。或者你可能希望'groupBy'粗略地将你的列表分组为[[param1],[err1,err2,err3],[param2],[err4],[param3]],然后再做更多的处理,通缉。 – chi

+0

你应该看看使用一个解析库,例如[Parsec](https://hackage.haskell.org/package/parsec-3.1.11/docs/Text-Parsec.html)使这种工作成为微风 – jkeuhlen

+0

这与我在代码审查的问题有一些相似之处,你可能想检查[有](http://codereview.stackexchange.com/questions/147874/kattis-speed-limit-read-irregular-input)。 – wizzup

回答

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"] 
+0

我不知道CIS194,这看起来很有趣。感谢代码和链接! – ols

+0

你可以标记答案为有用或投票回答:) – ArthurVard