我是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
来记录未解析的剩余字符串,并使用递归,但仍然无法执行。
所以我很感激任何提示! ^ _ ^
答案 0 :(得分:1)
F12
,F 12
,F1(a)
,F2a
,F5-A
,F34-5
这是一个不完整的描述,所以我会做一些猜测。
我首先要定义一个可以包含这些表达式的逻辑部分的类型。 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
,{ {1}},F1a1
等以及以下内容有效:F1-(a)
,F1A
,F1abc
,F1(abc)
,F1-abc
。这可能不是真的。 [1]
然后我会继续为每个子部分编写解析器并编写它们:
F1-a1b2
(正如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")
外卖:
您可以使用令牌解析(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
解析可选部分。
您可以使用symbol
或a <|> b <|> c
在组合器之间切换。
在选项之间交替时,请确保它们没有重叠的FIRST集。否则你需要try
;这是令人讨厌的,但有时是不可避免的。 (在这种情况下,三个选项的FIRST集合为choice [a, b, c]
,letter
和string "("
,即不重叠。)
[1]:为了限制,我坚持上述假设,但我觉得我也可以认为string "-"
,F1a-B
和F1(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"]