首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用attoparsec解析多行日志

用attoparsec解析多行日志
EN

Stack Overflow用户
提问于 2019-01-31 19:41:47
回答 1查看 214关注 0票数 1

我试图像这样解析多行日志

代码语言:javascript
复制
[xxx] This is 1
[xxx] This is also 1
[yyy] This is 2

我定义了这些类型

代码语言:javascript
复制
{-# LANGUAGE OverloadedStrings #-}

module Parser where

import Prelude hiding(takeWhile)
import Data.Text
import Data.Word
import Data.Attoparsec.Text as T
import Data.Char
import Data.String

data ID    = ID String deriving (Eq, Show)
data Entry = Entry ID String deriving (Eq, Show)
data Block = Block ID [String]
data Log   = Log [Block]

并定义了这些解析器:

代码语言:javascript
复制
parseID :: Parser ID
parseID = do
  char '['
  id <- takeTill ( == ']' )
  char ']'
  return $ ID $ unpack id

parseEntry :: Parser Entry
parseEntry = do
  id <- parseID
  char ' '
  content <- takeTill isEndOfLine
  return $ Entry id (unpack content)

当我做一些像parseOnly parseEntry entryString这样的事情,并得到一个Entry时,这是可行的。

问题是,当我试图解析类似我在开始时添加的日志时。我想要一个[Entry],但是我想要[Block]

另外,当两个或更多个连续行具有相同的ID (如xxx)时,应该将其存储在同一个块中,因此,为了解析上述日志,我想要返回

代码语言:javascript
复制
[block1, block2]
-- block1 == Block "xxx" ["This is 1", "This is also 1"]
-- block2 == Block "yyy" ["This is 2"]

如何使解析器创建新块或添加到最后生成的块中,取决于ID是否更改?

一个明显的解决方案是简单地生成一个[Entry],然后使用一个折叠函数将其转换为具有适当逻辑的[Block],但是我将执行两次传递,一次在日志上,另一次在[Entry]上,这似乎不仅对大型日志不太有效,而且感觉是错误的方法(根据我有限的attoparsec知识)。

还有其他想法吗?

编辑

Bob Dalgleish解决方案本质上是有效的(非常感谢!),只是需要一些调整才能使其工作。这是我的最后解决方案:

代码语言:javascript
复制
data ID    = ID String deriving (Eq, Show)
data Entry = Entry ID String deriving (Eq, Show)
data Block = Block ID [String] deriving (Eq, Show)
data Log   = Log [Block] deriving (Eq, Show)

parseID :: Parser ID
parseID = do
  char '['
  id <- takeTill ( == ']' )
  char ']'
  return $ ID $ unpack id

parseEntry :: Parser Entry
parseEntry = do
  id <- parseID
  char ' '
  content <- takeTill isEndOfLine
  return $ Entry id (unpack content)

parseEntryFor :: ID -> Parser Entry
parseEntryFor blockId = do
  id <- parseID
  if blockId == id
     then do
       char ' '
       content <- takeTill isEndOfLine
       endOfLine <|> endOfInput
       return $ Entry id (unpack content)
  else fail "nonmatching id"

parseBlock :: Parser Block
parseBlock = do
  (Entry entryId s) <- parseEntry
  let newBlock = Block entryId [s]
  endOfLine <|> endOfInput
  entries <- many' (parseEntryFor entryId)
  return $ Block entryId (s : Prelude.map (\(Entry _ s') -> s') entries)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-01-31 20:45:19

您需要为Block提供一个解析器。它接受一个Entry,使用相同的id查找Entry;如果不是相同的,它会追溯和返回到目前为止所拥有的内容。

首先,让我们介绍一个条件Entry解析器:

代码语言:javascript
复制
parseEntryFor :: ID -> Parser Entry
parseEntryFor blockId = do
  id <- parseEntry
  if blockId == id
  then do
         char ' '
         content <- takeTill isEndOfLine
         endOfLine
         return $ Entry id (unpack content)
  else fail "nonmatching id"

-- |A Block consists of one or more Entry's with the same ID
parseBlock :: Parser Block
parseBlock = do
  (Entry entryId s) <- parseEntry
  let newBlock = Block entryId [s]
  endOfLine
  entries <- many' (parseEntryFor entryId)
  return $ Block entryId s: (map (\(Entry _ s') -> x') entries)

(这段代码没有经过测试,因为我只使用过Parsec。)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54468116

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档