假设我有一个文档,文字由Jade样式的括号分隔,例如{{foo}}
。我编写了一个似乎正确提取foo
的Attoparsec解析器:
findFoos :: Parser [T.Text]
findFoos = many $ do
manyTill anyChar (string "{{")
manyTill letter (string "}}")
测试它表明它有效:
> parseOnly findFoos "{{foo}}"
Right ["foo"]
> parseOnly findFoos "{{foo}} "
Right ["foo"]
现在,使用Data.Conduit.Attoparsec
中的conduit-extra
模块,我似乎遇到了奇怪的行为:
> yield "{{foo}}" $= (mapOutput snd $ CA.conduitParser findFoos) $$ CL.mapM_ print
["foo"]
> yield "{{foo}} " $= (mapOutput snd $ CA.conduitParser findFoos) $$ CL.mapM_ print
-- floods stdout with empty lists
这是理想的行为吗?我应该在这里使用导管实用程序吗?对此有任何帮助都是巨大的!
答案 0 :(得分:1)
由于它使用many
,findFoos
会在没有找到任何分隔文字的情况下返回[]
而不会消耗输入。
另一方面,conduitParser
在流上重复应用解析器 ,返回每个已解析的值,直到它耗尽流。
"{{foo}} "
的问题是解析器将使用{{foo}}
,但是流中的空白区域仍然未被占用,因此解析器的进一步调用始终返回[]
。
如果您重新定义findFoos
一次使用一个带引号的元素,包括尾随空白,它应该有效:
findFoos' :: Parser String
findFoos' = do
manyTill anyChar (string "{{")
manyTill letter (string "}}") <* skipSpace
真实世界的例子在括号内的文本之间会有其他字符,因此在每次解析后跳过“额外的东西”(不会消耗下一个解析的任何{{
开口大括号)将会更加复杂。
也许以下内容可行:
findFoos'' :: Parser String
findFoos'' = do
manyTill anyChar (string "{{")
manyTill letter (string "}}") <* skipMany everythingExceptOpeningBraces
where
-- is there a simpler / more efficient way of doing this?
everythingExceptOpeningBraces =
-- skip one or more non-braces
(skip (/='{') *> skipWhile (/='{'))
<|>
-- skip single brace followed by non-brace character
(skip (=='{') *> skip (/='{'))
<|>
-- skip a brace at the very end
(skip (=='{') *> endOfInput)
(但是,如果流中没有任何括号内的文本,则此解析器将失败。也许您可以构建一个Parser (Maybe Text)
,在这种情况下返回Nothing
。)