Parsing 如何在不消耗(如parsec lookAhead)的情况下使Attoparsec解析器成功

Parsing 如何在不消耗(如parsec lookAhead)的情况下使Attoparsec解析器成功,parsing,haskell,attoparsec,Parsing,Haskell,Attoparsec,我编写了一个快速的attoparsec解析器来遍历一个aspx文件并删除所有的样式属性,它工作得很好,只是有一部分我不知道如何在不使用它的情况下成功地匹配 以下是我所拥有的: anyTill = manyTill anyChar anyBetween start end = start *> anyTill end styleWithQuotes = anyBetween (stringCI "style=\"") (stringCI "\"") styleWithoutQuotes =

我编写了一个快速的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
anyTill=manyTill anyChar
anyBetween start end=start*>anyTill end
styleWithQuotes=ANYBETH(stringCI“style=\”)(stringCI“\”)
styleWithoutQuotes=anyBetween(stringCI“style=)(stringCI”“”>)
everythingButStyles=manyTill anyChar(styleWithQuotes styleWithoutQuotes)many1 anyChar
我理解这部分是因为我在所有东西中都使用了manyTill,但是样式,这就是我如何积极地放弃所有样式的东西,但是在
styleWithoutQuotes
中,我需要它匹配“>”作为结束,而不是使用它,在parsec中,我只需要做
lookAhead”>
但我不能在atoparsec中实现这一点。

同时,添加了组合符,因此现在可以使用
前瞻(char'>')
前瞻(string“>”
来实现目标

下面是《泰晤士报》推出前的一个变通方法


您可以使用构建非消费解析器,它只查看下一个字节(如果有)。由于
ByteString
有一个
Monoid
实例,
Parser ByteString
是一个
MonadPlus
,您可以使用

lookGreater = do
    mbw <- peekWord8
    case mbw of
      Just 62 -> return ">"
      _ -> mzero
lookmorer=do
mbw返回“>”
_->mzero
(62是
'>'
'的代码点)查找
'>'
而不使用它或失败

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
然后

我们应该做到,我们可以重写

styleWithoutQuotes = anyBetween' (stringCI "style=") [' ','>']
如果您想让它吃
'
而不是
'>'
,您可以在后面显式吃空格:

styleWithoutQuotes = anyBetween' (stringCI "style=") [' ','>'] 
                     <* A.takeWhile isSpace
但我认为从你所说的来看,你希望
样式不带引号
成为一个混合体;它吃
'
,但不吃

fromUptoEat startP endChars eatChars = 
            startP 
            *> takeTill (flip elem endChars) 
            <* satisfy (flip elem eatChars)
整体解析器
everythingButStyles
使用
的方式意味着,如果找不到
“style”
,它将回溯,然后获取所有内容。这是一个缓慢的例子。问题是我们失败得太晚——在输入字符串的末尾,这是一个不好的时机来选择是否应该失败。让我们全力以赴,努力

  • 如果我们要失败,就马上失败
  • 最大限度地利用来自
  • 想法:直到我们得到一个s,然后跳过的风格,如果有一个

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

    很好用!谢谢我想我可以用peek来做这个,但是我想不出如何让它返回正确的字符串类型(我现在必须去读关于mzero的书,我仍然不太熟悉所有的类型类和它们的实例。如果我强迫自己通过它,恐怕typeclassopedia会吞噬我的灵魂)最后你会找回你的灵魂。稍微改变。是的,我知道takeWhile应该更有效,但从我尝试做的事情来看,takeWhile似乎只允许在单个字节上进行匹配,而您可以看到,我的大多数匹配都是在字符串上进行的。我是否误解了如何使用takeWhile?谢谢你在这里的贡献well@JimmyHoffa您的开始都与字符串匹配(而且
    string
    是有效的,所以这没关系)。您的结尾(在示例代码中)都是单个字符,或者是其中的
    s,这就是为什么我允许列出结尾字符。编辑澄清了吗?啊,你说得对。很酷,所以基本上很多和字符串都是你想要匹配很多字符串的正确方法,但是当你尝试匹配单字符时,获取和满足是正确的方法吗?@jimmyhoff。单次使用“满足”是可以的,但是当你想使用“满足”多个时,你应该使用takeWhile或takeTill。(也是新编辑。)由于
    styleWithQuotes'
    styleWithoutQuotes'
    重叠(它们都是从解析字符串“style=”开始的),您还可以将
    styleWithQuotes'styleWithoutQuotes'
    重构为一个解析器,以减少回溯的需要。哪种流类型(
    ByteString
    Text
    )您使用的不清楚,但是,如果您使用的是文本,您应该使用
    ascicii
    ,而不是
    stringCI
    ;后者现在是。
    fromUptoEat startP endChars eatChars = 
                startP 
                *> takeTill (flip elem endChars) 
                <* satisfy (flip elem eatChars)
    
    styleWithQuotes' = fromUptoIncl (stringCI "style=\"") "\""
    styleWithoutQuotes' = fromUptoEat (stringCI "style=") " >" " "
    
    notStyleNotEvenS = takeTill (flip elem "sS") 
    skipAnyStyle = (styleWithQuotes' <|> styleWithoutQuotes') *> notStyleNotEvenS 
                   <|> cons <$> anyChar <*> notStyleNotEvenS
    
    noStyles = append <$> notStyleNotEvenS <*> many skipAnyStyle 
    
    parseNoStyles = parseOnly noStyles