我写了一个快速的attoparsec解析器来遍历一个aspx文件并删除所有的样式属性,它的工作正常,除了它的一部分,我无法弄清楚如何使它成功匹配>
没有消耗它。
这就是我所拥有的:
anyTill = manyTill anyChar
anyBetween start end = start *> anyTill end
styleWithQuotes = anyBetween (stringCI "style=\"") (stringCI "\"")
styleWithoutQuotes = anyBetween (stringCI "style=") (stringCI " " <|> ">")
everythingButStyles = manyTill anyChar (styleWithQuotes <|> styleWithoutQuotes) <|> many1 anyChar
我理解这部分是因为我在everythingButStyles中如何使用manyTill,这就是我如何积极地放弃所有样式的东西,但在styleWithoutQuotes
我需要它来匹配“&gt;”作为一个结束,但不消耗它,在parsec中我会完成lookAhead ">"
但我不能在attoparsec中这样做。
答案 0 :(得分:5)
anyBetween start end = start *> anyTill end
你的anyBetween
解析器吃了它的最后一个字符因为anyTill
确实 - 它被设计为解析结束标记,但假设你不想让输入中的右括号再次解析。
请注意,您的end
解析器都是单字符解析器,因此我们可以更改功能以使用它:
anyBetween'' start ends = start *> many (satisfy (not.flip elem ends))
但是many
效率不如Attoparsec的takeWhile
,你应该尽可能多地使用它,所以如果你已经完成了
import qualified Data.Attoparsec.Text as A
然后
anyBetween' start ends = start *> A.takeWhile (not.flip elem ends)
应该做的伎俩,我们可以重写
styleWithoutQuotes = anyBetween' (stringCI "style=") [' ','>']
如果你希望它吃' '
而不是'>'
你可以明确地吃掉空格:
styleWithoutQuotes = anyBetween' (stringCI "style=") [' ','>']
<* A.takeWhile isSpace
takeWhile
也许styleWithQuotes
也可以通过重写来使用takeWhile
,所以让我们在anyBetween
的行上创建两个帮助器。它们从起始解析器到结束字符,并且有包容性和独占版本:
fromUptoExcl startP endChars = startP *> takeTill (flip elem endChars)
fromUptoIncl startP endChars = startP *> takeTill (flip elem endChars) <* anyChar
但我想从你所说的话来看,你希望styleWithoutQuotes
成为一个混合体;它吃' '
但不吃>
:
fromUptoEat startP endChars eatChars =
startP
*> takeTill (flip elem endChars)
<* satisfy (flip elem eatChars)
(所有这些都假设您的结束字符列表中包含少量字符,否则elem
效率不高 - 如果您检查的是大型列表,则会有一些Set
变体字母表。)
现在进行重写:
styleWithQuotes' = fromUptoIncl (stringCI "style=\"") "\""
styleWithoutQuotes' = fromUptoEat (stringCI "style=") " >" " "
everythingButStyles
以某种方式使用<|>
,这意味着如果找不到"style"
,它将会回溯,然后取走所有内容。这是一个可能很慢的事情的例子。问题是我们迟到了 - 在输入字符串的末尾,这是一个关于我们是否应该失败的选择的不好时机。让我们全力以赴,尝试
想法:直到我们得到一个s,然后跳过那个样式的那个样式。
notStyleNotEvenS = takeTill (flip elem "sS")
skipAnyStyle = (styleWithQuotes' <|> styleWithoutQuotes') *> notStyleNotEvenS
<|> cons <$> anyChar <*> notStyleNotEvenS
anyChar
通常是s
或S
,但没有必要再检查一下。
noStyles = append <$> notStyleNotEvenS <*> many skipAnyStyle
parseNoStyles = parseOnly noStyles
答案 1 :(得分:4)
同时,lookAhead
组合符已添加到attoparsec,因此现在可以使用lookAhead (char '>')
或lookAhead (string ">")
来实现目标。
以下是介绍之前的解决方法。
您可以使用peekWord8
构建非消费解析器,它只查看下一个字节(如果有的话)。由于ByteString
有Monoid
个实例,Parser ByteString
是MonadPlus
,您可以使用
lookGreater = do
mbw <- peekWord8
case mbw of
Just 62 -> return ">"
_ -> mzero
(62是'>'
的代码点)要么找到'>'
而不消耗它或失败。