首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >stringCI的Attoparsec /=版本

stringCI的Attoparsec /=版本
EN

Stack Overflow用户
提问于 2013-04-10 04:41:15
回答 1查看 191关注 0票数 1

我正在解析robots.txt文件,并且我已经编写了解析器来成功解析“格式良好”的robots.txt文件。我已经能够调整解析器以跳过以符号(如#或/代表注释)开头的行,但只能使用inClass "#/"

我一直无法解决的一个问题是,如果一行不包含我想要匹配的字符串,则跳过该行。

代码语言:javascript
复制
User-agent: *
Disallow: /includes/
Disallow: /misc/
Disallow: /modules/
Doesn't belong here
Disallow: /profiles/
Disallow: /scripts/
Disallow: /themes/

我首先尝试使用以下命令进行匹配:

satisfy (notInClass "DdUu") *> skipWhile (not . isEndOfLine)

我认为这样做会消除我对特定注释行解析器的需求,因为散列或斜杠不会落入字符类。问题是,这不起作用。

我也意识到,如果它工作了,它无论如何也不会工作,因为它不能解决像“不允许”这样的匹配问题。“不允许”。

以下是解析代码(没有注释跳过代码,这只适用于格式良好的robots.txt):

代码语言:javascript
复制
{-# LANGUAGE OverloadedStrings, RankNTypes #-}

import           Prelude hiding (takeWhile)
import           Control.Applicative hiding (many)
import           Data.Char
import           Data.Text as T hiding (toLower)
import           Data.Text.Encoding as E
import           Control.Monad
import           Data.Attoparsec.ByteString
import qualified Data.Attoparsec.Char8 as AC
import           Data.Array.Unboxed
import           Data.ByteString as B hiding (takeWhile)
import qualified Data.ByteString.Internal as BI
import           Data.Word (Word8)

type RuleMap = [(ByteString, ByteString)]

-- newtype for indexable ua
newtype UserAgent = UserAgent { unUA :: ByteString }
    deriving (Eq, Ord, Show)

data RuleSet = RuleSet
    { userAgent :: UserAgent,
      rules     :: RuleMap }
     deriving (Eq, Ord, Show)

main = do
    r <- B.readFile "/Users/ixmatus/Desktop/robots.txt"
    print $ parse (many1 parseUABlock) r

stripper = E.encodeUtf8 . T.strip . E.decodeUtf8

isNotEnd = not . AC.isEndOfLine

-- | Catch all character matching, basically
matchALL :: Word8 -> Bool
matchALL = inClass ":/?#[]@!$&'()*%+,;=.~a-zA-Z0-9 _-"

-- | @doParse@ Run the parser, complete the partial if the end of the stream has
-- a newline with an empty string
doParse :: ByteString -> [RuleSet]
doParse cont =
    case parse (many1 parseUABlock) cont of
        Done _ set -> set
        Partial f -> handlePartial (f B.empty)
        Fail {} -> []

-- | @handlePartial@ Handle a partial with empty string by simply
-- returning the last completion
handlePartial :: forall t a. IResult t [a] -> [a]
handlePartial (Done _ r) = r
handlePartial (Fail {})  = []

-- | @parseUABlock@ Parse a user-agent and rules block
parseUABlock = do
    ua    <- parseUACol *> uA
    rulez <- many1 parseRules

    return RuleSet { userAgent = UserAgent ua,
                     rules = rulez }

-- | @matchUACol@ Parse the UA column and value taking into account
-- possible whitespace craziness
parseUACol = AC.skipSpace
          *> AC.stringCI "User-Agent"
          <* AC.skipSpace
          *> AC.char8 ':'
          *> AC.skipSpace

uA = do
    u <- takeWhile1 isNotEnd
    return (stripper u)

-- | @parseRules@ Parse the directives row
parseRules = (,) <$> parseTransLower
             <*> directiveRule

directiveRule = do
    rule <- takeWhile1 matchALL <* many1 AC.endOfLine

    return (stripper rule)

parseTransLower = do
    res <- parseDirectives <* AC.skipSpace
    return (lowercase res)

ctypeLower = listArray (0,255) (Prelude.map (BI.c2w . toLower) ['\0'..'\255']) :: UArray Word8 Word8
lowercase = B.map (\x -> ctypeLower!x)

directives = AC.stringCI "Disallow" <|> AC.stringCI "Allow"

-- | @parseDirectives@ Parse the directive column and any possibly
-- funny whitespace
parseDirectives = AC.skipSpace
                  *> directives -- <|> AC.stringCI "Crawl-delay" <|> AC.stringCI "Sitemap")
                  <* AC.skipSpace
                  <* AC.char8 ':'
EN

回答 1

Stack Overflow用户

发布于 2013-04-10 13:08:53

考虑这种方法。

定义:

代码语言:javascript
复制
data RobotsDirective = RobotsDirective String String

这表示robots.txt文件中已解析的指令。第一个字符串是指令(即UserAgentAllowDisallow等)。第二个字符串是冒号后面的东西。

现在为RobotsDirective编写一个解析器

代码语言:javascript
复制
parseRD :: Parser RobotsDirective

parseRD将查找指令名称(应该只包含字母、数字和破折号,可能还有下划线),后跟一个冒号,后跟零个或多个非换行符。适当地忽略空格。如果parseRD找到这样的模式,它将创建并返回一个RobotsDirective。否则,它将跳过一行字符并重试。

现在您已经有了一个用于RobotsDirective的解析器,您可以用标准方式创建一个用于[RobotsDirective]的解析器。

这个解析器简单地跳过任何看起来不像指令的行,这将包括空行、注释行和以Don't allow...开头的行。但是,对于robots.txt文件中无效的行,它可能会返回一个RobotsDirective,即:

代码语言:javascript
复制
foo: blah

将返回RobotsDirective "foo" "blah"。在解析robots.txt文件并获得RobotsDirective值的列表之后,只需遍历该列表并忽略您不感兴趣的值。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15912086

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档