如何使用parsec获取字符串

时间:2018-04-17 03:33:08

标签: string parsing haskell

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

  

"我想从F1(a),F2a,F5-A,F34-5等选择F12或F12,   但不应该选择F,即选择以F开头的那些   后跟一个数字(在数字之前可能有零个或多个空格),然后是[' a' ...' z'] ++中的任何字符   [' A' ...'] + [' 0' ...' 9'] ++ ['( '&#39)'" - "。]"

结果应该是[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 个答案:

答案 0 :(得分:1)

  

F12F 12F1(a)F2aF5-AF34-5

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

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

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

    也就是说,“F”后跟一个数字和一个可选部分,它是字母,带括号的字母或短划线后跟字母/数字。由于“F”之后的数字可以有多个数字,我假设可选的字母/数字也可能是多个。

    由于示例有限,我认为以下内容无效:F1a(b)F1(a)bF1a-5F1(a)-AF1a(a)-5,{ {1}},F1a1等以及以下内容有效:F1-(a)F1AF1abcF1(abc)F1-abc。这可能不是真的。 [1]

  2. 然后我会继续为每个子部分编写解析器并编写它们:

    F1-a1b2
  3. (正如Jon Purdy在评论中写的那样)在字符串上使用此解析器来获取多个匹配项,

    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
          ]
    

    打印:

    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")
    
  4. 外卖:

    • 您可以使用令牌解析(Right [F (12,Nothing),F (12,Nothing),F (1,Just "a"),F (2,Just "a"),F (5,Just "A"),F (34,Just "5")] )解析可选空格。

    • 您可以使用option, optionMaybe or optional解析可选部分。

    • 您可以使用symbola <|> b <|> c在组合器之间切换。

    • 在选项之间交替时,请确保它们没有重叠的FIRST集。否则你需要try;这是令人讨厌的,但有时是不可避免的。 (在这种情况下,三个选项的FIRST集合为choice [a, b, c]letterstring "(",即不重叠。)

    [1]:为了限制,我坚持上述假设,但我觉得我也可以认为string "-"F1a-BF1(a)-5是有效的,在这种情况下,我可能会将模型更改为:

    F1(a)-5A

答案 1 :(得分:0)

我们可以使用以下命令在字符串中获取特定模式的子字符串 findAll 来自的组合器 replace-megaparsec

请注意,此tuhao解析器实际上并未返回任何内容。 findAll组合器仅检查解析器是否成功,以查找与模式匹配的子字符串。

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"]