anyBetween start end = start *> anyTill end
您的anyBetween
解析器会吃掉它的最后一个字符,因为anyTill
它的设计目的是解析到一个结束标记,但假设您不想在输入中保留右大括号以再次解析。
请注意,您的end
解析器都是单字符解析器,因此我们可以更改功能以使用它:
anyBetween'' start ends = start *> many (satisfy (not.flip elem ends))
但many
不如 Attoparsec's 高效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"
它会回溯然后拿走所有东西。这是一个可能很慢的例子。问题是我们失败得很晚 - 在输入字符串的末尾,这是一个糟糕的时间来选择我们是否应该失败。让我们全力以赴尝试
- 如果我们要失败,就直接失败。
- 最大限度地使用来自Data.Attoparsec.Text.Internal的更快的解析器
想法:直到我们得到一个 s,然后如果那里有一个样式,则跳过该样式。
notStyleNotEvenS = takeTill (flip elem "sS")
skipAnyStyle = (styleWithQuotes' <|> styleWithoutQuotes') *> notStyleNotEvenS
<|> cons <$> anyChar <*> notStyleNotEvenS
通常anyChar
是s
or S
,但再次检查是没有意义的。
noStyles = append <$> notStyleNotEvenS <*> many skipAnyStyle
parseNoStyles = parseOnly noStyles