如何用Parsec读取精确的N个字符?

时间:2013-03-18 10:01:28

标签: haskell parsec

我是Haskell和Parsec的新手。 我希望解析字符串的php-serialize格式:numb:“string”;'喜欢

s:12:“123”; 6789012“;

其中number是字符数。 所以,功能看起来像:

newtype PhpString = PhpString String

pString :: GenParser Char st PhpString
pString = do { string "s:"
        ; value1 <- many1 digit
        ; string ":\""
        ; value2 <- takeExactNChars (read value1) 
        ; string "\";"      
        ; return $ PhpString value2
    }
    where 
        takeExactNChars n = ???????

2 个答案:

答案 0 :(得分:9)

正如Sarah所说,惯用parsec解决方案是使用count组合器:

newtype PhpString = PhpString String

pString :: Parser PhpString
pString = do
  string "s:"
  value1 <- many1 digit
  string ":\""
  value2 <- count (read value1) 
  string "\";"      
  return $ PhpString value2

我们可以更进一步清理这个解析器,如果您感兴趣的话,也可以更简洁:

import Control.Applicative (empty)
import Text.Read

pString :: Parser PhpString
pString = do
  len <- readMaybe <$> (string "s:" *> many1 digit)
  case len of
    Just n -> PhpString <$> string ":\"" *> count n anyChar <* string "\";"
    Nothing -> empty

或者甚至可能:

pString :: Parser PhpString
pString =
  readMaybe <$> (string "s:" *> many1 digit) >>=
    maybe empty $ \n ->
      PhpString <$> string ":\"" *> count n anyChar <* string "\";"
如果empty失败,来自Control.Alternative

read会失败解析器。

答案 1 :(得分:5)

我会使用Control.Monad中的replicateM来编写它:

import Text.ParserCombinators.Parsec
import Control.Monad (replicateM)

pString :: Parser String
pString = do string "s:"
             n <- fmap read (many1 digit)
             string ":\""         -- Bug fix; you weren't picking up the colon
             s <- replicateM n anyChar
             string "\";"
             return s

在ghci中测试:

*Main> parse pString "" "s:12:\"123\";6789012\";"
Right "123\";6789012"