Conduit 和 Attoparsec - 提取分隔文本
Conduit and Attoparsec - extracting delimited text
假设我有一个文档,其中的文本由 Jade 风格的括号分隔,例如 {{foo}}
。我写了一个 Attoparsec 解析器,似乎可以正确提取 foo
:
findFoos :: Parser [T.Text]
findFoos = many $ do
manyTill anyChar (string "{{")
manyTill letter (string "}}")
测试表明它有效:
> parseOnly findFoos "{{foo}}"
Right ["foo"]
> parseOnly findFoos "{{foo}} "
Right ["foo"]
现在,有了 conduit-extra
中的 Data.Conduit.Attoparsec
模块,我似乎 运行 出现了奇怪的行为:
> 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
这是期望的行为吗?我应该在这里使用管道实用程序吗?对此的任何帮助都是巨大的!
因为它使用 many
,findFoos
将 return []
在找不到任何分隔文本时不消耗输入。
另一方面,conduitParser
对流重复 应用解析器,return 分析每个已解析的值,直到耗尽流。
"{{foo}} "
的问题是解析器将消耗 {{foo}}
,但流中的空白 space 仍未被消耗,因此解析器的进一步调用总是 return []
.
如果您重新定义 findFoos
以一次使用一个带引号的元素,包括尾随空格,它应该可以工作:
findFoos' :: Parser String
findFoos' = do
manyTill anyChar (string "{{")
manyTill letter (string "}}") <* skipSpace
现实世界的例子在括号文本之间会有其他字符,所以在每次解析后跳过 "extra stuff"(不消耗任何 {{
左大括号进行下一次解析)会有点更多参与。
也许像下面这样的东西会起作用:
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)
that returns Nothing
在这种情况下.)
假设我有一个文档,其中的文本由 Jade 风格的括号分隔,例如 {{foo}}
。我写了一个 Attoparsec 解析器,似乎可以正确提取 foo
:
findFoos :: Parser [T.Text]
findFoos = many $ do
manyTill anyChar (string "{{")
manyTill letter (string "}}")
测试表明它有效:
> parseOnly findFoos "{{foo}}"
Right ["foo"]
> parseOnly findFoos "{{foo}} "
Right ["foo"]
现在,有了 conduit-extra
中的 Data.Conduit.Attoparsec
模块,我似乎 运行 出现了奇怪的行为:
> 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
这是期望的行为吗?我应该在这里使用管道实用程序吗?对此的任何帮助都是巨大的!
因为它使用 many
,findFoos
将 return []
在找不到任何分隔文本时不消耗输入。
另一方面,conduitParser
对流重复 应用解析器,return 分析每个已解析的值,直到耗尽流。
"{{foo}} "
的问题是解析器将消耗 {{foo}}
,但流中的空白 space 仍未被消耗,因此解析器的进一步调用总是 return []
.
如果您重新定义 findFoos
以一次使用一个带引号的元素,包括尾随空格,它应该可以工作:
findFoos' :: Parser String
findFoos' = do
manyTill anyChar (string "{{")
manyTill letter (string "}}") <* skipSpace
现实世界的例子在括号文本之间会有其他字符,所以在每次解析后跳过 "extra stuff"(不消耗任何 {{
左大括号进行下一次解析)会有点更多参与。
也许像下面这样的东西会起作用:
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)
that returns Nothing
在这种情况下.)