5

我写了一个快速的 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

我理解这部分是因为我如何在所有内容中使用 manyTillButStyles,这就是我积极地将所有样式的东西放在地上的方式,但styleWithoutQuotes我需要它以匹配“>”作为结尾,但不消耗它,以秒为单位我本来会这样做lookAhead ">",但我不能在 attoparsec 中这样做。

4

2 回答 2

5

同时,在attoparseclookAhead中添加了组合器,因此现在可以使用or来实现目标。lookAhead (char '>')lookAhead (string ">")

以下是引入之前的解决方法。


您可以使用 构建非消耗解析器peekWord8,它只查看下一个字节(如果有)。因为ByteString有一个Monoid实例,Parser ByteString所以是一个MonadPlus,你可以使用

lookGreater = do
    mbw <- peekWord8
    case mbw of
      Just 62 -> return ">"
      _ -> mzero

(62 是 的代码点'>')要么找到 a'>'而不消耗它,要么失败。

于 2012-11-02T23:08:42.587 回答
5
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"它会回溯然后拿走所有东西。这是一个可能很慢的例子。问题是我们失败得很晚 - 在输入字符串的末尾,这是一个糟糕的时间来选择我们是否应该失败。让我们全力以赴尝试

  1. 如果我们要失败,就直接失败。
  2. 最大限度地使用来自Data.Attoparsec.Text.Internal的更快的解析器

想法:直到我们得到一个 s,然后如果那里有一个样式,则跳过该样式。

notStyleNotEvenS = takeTill (flip elem "sS") 
skipAnyStyle = (styleWithQuotes' <|> styleWithoutQuotes') *> notStyleNotEvenS 
               <|> cons <$> anyChar <*> notStyleNotEvenS

通常anyCharsor S,但再次检查是没有意义的。

noStyles = append <$> notStyleNotEvenS <*> many skipAnyStyle 

parseNoStyles = parseOnly noStyles
于 2012-11-02T23:57:32.410 回答