代码之家  ›  专栏  ›  技术社区  ›  Z-Y.L

如何使用parsec获取字符串中特定模式的子字符串

  •  4
  • Z-Y.L  · 技术社区  · 6 年前

    我是Haskell的新手,现在我正在学习使用parsec。我陷入了一个问题,那就是,我想得到满足字符串中某个特定模式的所有子字符串。例如,从以下字符串,

    “我想从F1(a)、F2a、F5-a、F34-5等中选择F12或F 12, 但不能只选择F,也就是说,选择那些以F开头的 后跟一个数字(数字之前可能有零个或多个空格),然后是['a'..'z']中的任何字符++ ['A'..'Z']+++['0'..'9']+++['(',')',“-”]。“”

    结果应为[F12、F12、F1(a)、F2a、F5-a、F34-5],其中F和数字之间的空格应删除。

    使用parsec,我成功地获得了一个子字符串,如F12、F2a。代码如下:

    hao :: Parser Char
    hao = oneOf "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ()-"
    tuhao :: Parser String
    tuhao = do { c <- char 'F'
               ; many space
               ; c1 <- digit
               ; cs <- many hao
               ; return (c:c1:cs)
               }
    parse tuhao "" str -- can parse the str and get one sub-string.
    

    然而,我一直在思考如何解析上面的示例字符串并获取特定模式的所有子字符串。我的想法是,如果找到F,则开始解析,否则跳过解析,或者如果解析失败,则跳过解析。但我不知道如何实施这个计划。我有另一个想法 State 记录未分析的剩余字符串,并使用递归,但仍无法执行。

    所以我很感激你的任何建议^_^

    2 回复  |  直到 6 年前
        1
  •  1
  •   sshine    6 年前

    F12 ,则, F 12 ,则, F1(a) ,则, F2a ,则, F5-A ,则, F34-5

    这是一个不完整的描述,所以我会做一些猜测。

    1. 我将首先定义一个类型,该类型可以包含这些表达式的逻辑部分。E、 g。

      newtype F = F (Int, Maybe String) deriving Show
      

      也就是说,“F”后跟一个数字和可选部分,可以是字母、括号中的字母,也可以是破折号后跟字母/数字。由于“F”后面的数字可以有多个数字,我假设可选字母/数字也可以是多个。

      由于示例有限,我假设以下内容无效: F1a(b) ,则, F1(a)b ,则, F1a-5 ,则, F1(a)-A ,则, F1a(a)-5 ,则, F1a1 ,则, F1-(a) ,并确认以下各项有效: F1A ,则, F1abc ,则, F1(abc) ,则, F1-abc ,则, F1-a1b2 .这可能不是真的。[1]

    2. 然后,我将继续为这些子部分中的每一个子部分编写解析器,并编写它们:

      module Main where
      
      import Text.Parsec
      import Data.Maybe (catMaybes)
      
      symbol :: String -> Parser String
      symbol s = string s <* spaces
      
      parens :: Parser a -> Parser a
      parens = between (string "(") (string ")")
      
      digits :: Parser Int
      digits = read <$> many1 digit
      
      parseF :: Parser F
      parseF = curry F <$> firstPart <*> secondPart
        where
          firstPart :: Parser Int
          firstPart = symbol "F" >> digits
      
          secondPart :: Parser (Maybe String)
          secondPart = optionMaybe $ choice
            [ many1 letter
            , parens (many1 letter)
            , string "-" >> many1 alphaNum
            ]
      
    3. (乔恩·珀迪在评论中写道,) 在字符串上使用此解析器获取多个匹配项,

      extract :: Parser a -> Parser [a]
      extract p = do (:) <$> try p <*> extract p
              <|> do anyChar >> extract p
              <|> do eof >> return []
      
      readFs :: String -> Either ParseError [F]
      readFs s = parse (extract parseF) "" s
      
      main :: IO ()
      main = print (readFs "F12, F 12, F1(a), F2a, F5-A, F34-5")
      

      此打印:

      Right [F (12,Nothing),F (12,Nothing),F (1,Just "a"),F (2,Just "a"),F (5,Just "A"),F (34,Just "5")]
      

    外卖:

    • 您可以使用标记解析来解析可选的空格( symbol )。

    • 您可以使用 option , optionMaybe or optional

    • 您可以使用 a <|> b <|> c choice [a, b, c]

    • 当在两个选项之间交替时,确保它们没有重叠的第一组。否则你需要 try ;这很令人讨厌,但有时不可避免。(在这种情况下,三个选项的第一组是 letter ,则, string "(" string "-" ,即不重叠。)

    [1] :为了限制,我坚持上面的假设,但我觉得我也可以假设 F1a-B ,则, F1(a)-5 F1(a)-5A 有效,在这种情况下,我可能会将模型更改为:

    newtype F = F (Int, Maybe String, Maybe String)
    
        2
  •  0
  •   James Brock    5 年前

    我们可以使用 findAll 组合器来自 replace-megaparsec

    请注意 tuhao 解析器实际上不会返回任何内容。这个 芬德尔 combinator只是检查解析器是否成功找到与模式匹配的子字符串。

    import Replace.Megaparsec
    import Text.Megaparsec
    import Text.Megaparsec.Char
    import Data.Maybe
    import Data.Either
    
    let tuhao :: Parsec Void String ()
        tuhao = do
            void $ single 'F'
            void $ space
            void $ digitChar
            void $ many $ oneOf "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ()-"
    
    input = "I want to choose F12 or F 12 from F1(a), F2a, F5-A, F34-5 and so on, but F alone should not be chosen, that is, choose those which start with F followed by a digit (before the digit there could be zero or more than one space) and then by any character from ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ ['(',')',\"-\"]."
    
    rights $ fromJust $ parseMaybe (findAll tuhao) input
    
    ["F12","F 12","F1(a)","F2a","F5-A","F34-5"]