使用 attoparsec 进行多行后续行的匹配值
Matching values that carry onto multiple following lines with attoparsec
我正在尝试解析以下内容:
message: 123 test
abc xys
messageA: hmm
messageA: testing
messageB: aueo
qkhwueoaz
变成这样的东西:
[
("message", "123 test\nabcxyz"),
, ("messageA", "hmm")
, ("messageA","testing")
, ("messageB","aueo\nqkhwueoaz")
]
但是我似乎无法弄清楚这一点,我发现了一些困难,因为我不是 100% 熟悉 attoparsecs 功能(而且我真的看不到每个函数是否被记录在案它向前移动光标...)。
我已通读: 并且我得到以下代码:
isChrisNext :: Parser ()
isChrisNext = lookAhead (parseChris) *> pure()
notFollowedBy :: Monad m => m a -> m b
notFollowedBy p = p >> fail "not followed by"
restOfLine :: Parser Text
restOfLine = do
rest <- takeTill (== '\n')
isEOF <- atEnd
if isEOF then
return rest
else
(char '\n') >> return rest
parseChris :: Parser [Text]
parseChris = do
x <- takeWhile1 (notInClass ":")
_ <- string ":"
x' <- manyTill restOfLine (endOfInput <|> isChrisNext)
() <- return $ unsafePerformIO $! do
print "?????????????"
print x
print x'
return $ x : x'
然而,试图用 parseChris
解析数据只是 returns:
[ "message" ]
而我期待 ("message", "123 test\nabcxyz")
。
如果我将前瞻函数更改为:
isChrisNext :: Parser ()
isChrisNext = lookAhead (string "message:") *> pure()
我得到了更符合预期的输出:
[ "message"
, "123 test"
, "abc xys"
]
另外,前面提到的问题还有一个评论建议的做法是:
Just parse the log times apart by matching on time stamps, and only
within each time-entry parse the sub-entries.
我也知道第二行可能包含 :
的潜在问题,但这不是我需要考虑的问题,谢天谢地...
我发现在使用解析器组合器时真正有用的方法是将整个问题分解成更小的部分。所以,我只是先组成解析器 bottom-up: a keyValuePair
,然后整个解析器只包含 many keyValuePair
。 keyValuePair
消耗了 :
之前的部分,然后尽可能多地吃掉没有 :
的行。
在代码中:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Char8 as BS
import qualified Data.Attoparsec.ByteString.Char8 as AT
import Control.Applicative
import Data.Functor
valuePart :: AT.Parser BS.ByteString
valuePart = AT.takeTill (`BS.elem` ":\n") <* AT.endOfLine
keyValuePair :: AT.Parser (BS.ByteString, BS.ByteString)
keyValuePair = do
key <- AT.takeTill (== ':')
void ": "
valLines <- AT.many1 valuePart
pure (key, BS.intercalate "\n" valLines)
parser :: AT.Parser [(BS.ByteString, BS.ByteString)]
parser = many keyValuePair
运行 根据您的输入数据生成
*Main> AT.parseOnly parser test
Right [("message","123 test\nabc xys"),("messageA","hmm"),("messageA","testing"),("messageB","aueo")]
请注意,没有前瞻,因为不需要它:一旦 valuePart
遇到 :
,它就会失败,这会导致 keyValuePair
停止并且接下来 keyValuePair
通过 parser
.
中的 top-level many
得到 运行
顺便说一句,您可以使用 Debug.Trace
中的 trace
和 traceShow
而不是 unsafePerformIO
来生成调试输出。
我正在尝试解析以下内容:
message: 123 test
abc xys
messageA: hmm
messageA: testing
messageB: aueo
qkhwueoaz
变成这样的东西:
[
("message", "123 test\nabcxyz"),
, ("messageA", "hmm")
, ("messageA","testing")
, ("messageB","aueo\nqkhwueoaz")
]
但是我似乎无法弄清楚这一点,我发现了一些困难,因为我不是 100% 熟悉 attoparsecs 功能(而且我真的看不到每个函数是否被记录在案它向前移动光标...)。
我已通读:
isChrisNext :: Parser ()
isChrisNext = lookAhead (parseChris) *> pure()
notFollowedBy :: Monad m => m a -> m b
notFollowedBy p = p >> fail "not followed by"
restOfLine :: Parser Text
restOfLine = do
rest <- takeTill (== '\n')
isEOF <- atEnd
if isEOF then
return rest
else
(char '\n') >> return rest
parseChris :: Parser [Text]
parseChris = do
x <- takeWhile1 (notInClass ":")
_ <- string ":"
x' <- manyTill restOfLine (endOfInput <|> isChrisNext)
() <- return $ unsafePerformIO $! do
print "?????????????"
print x
print x'
return $ x : x'
然而,试图用 parseChris
解析数据只是 returns:
[ "message" ]
而我期待 ("message", "123 test\nabcxyz")
。
如果我将前瞻函数更改为:
isChrisNext :: Parser ()
isChrisNext = lookAhead (string "message:") *> pure()
我得到了更符合预期的输出:
[ "message"
, "123 test"
, "abc xys"
]
另外,前面提到的问题还有一个评论建议的做法是:
Just parse the log times apart by matching on time stamps, and only within each time-entry parse the sub-entries.
我也知道第二行可能包含 :
的潜在问题,但这不是我需要考虑的问题,谢天谢地...
我发现在使用解析器组合器时真正有用的方法是将整个问题分解成更小的部分。所以,我只是先组成解析器 bottom-up: a keyValuePair
,然后整个解析器只包含 many keyValuePair
。 keyValuePair
消耗了 :
之前的部分,然后尽可能多地吃掉没有 :
的行。
在代码中:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Char8 as BS
import qualified Data.Attoparsec.ByteString.Char8 as AT
import Control.Applicative
import Data.Functor
valuePart :: AT.Parser BS.ByteString
valuePart = AT.takeTill (`BS.elem` ":\n") <* AT.endOfLine
keyValuePair :: AT.Parser (BS.ByteString, BS.ByteString)
keyValuePair = do
key <- AT.takeTill (== ':')
void ": "
valLines <- AT.many1 valuePart
pure (key, BS.intercalate "\n" valLines)
parser :: AT.Parser [(BS.ByteString, BS.ByteString)]
parser = many keyValuePair
运行 根据您的输入数据生成
*Main> AT.parseOnly parser test
Right [("message","123 test\nabc xys"),("messageA","hmm"),("messageA","testing"),("messageB","aueo")]
请注意,没有前瞻,因为不需要它:一旦 valuePart
遇到 :
,它就会失败,这会导致 keyValuePair
停止并且接下来 keyValuePair
通过 parser
.
many
得到 运行
顺便说一句,您可以使用 Debug.Trace
中的 trace
和 traceShow
而不是 unsafePerformIO
来生成调试输出。