2013-03-18 2 views
6

Я новичок в Haskell и Parsec. Я хочу разобрать php-serialize формат строки: numb: "string"; какКак читать точные N символов с Parsec?

s: 12: "123"; 6789012" ;

, где число подсчет символов Таким образом, функция выглядит следующим образом:

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 = ??????? 
+5

Parsec имеет комбинатор под названием 'count', который выполняет именно это. Это эквивалентно 'replicateM'. – Sarah

+0

@ Сара хорошая, я не знал об этом. –

+0

@all: большое спасибо! – viorior

ответ

4

Я хотел бы написать его с помощью replicateM от Control.. Монада:

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

Как уже упоминалась Сара, идиоматические 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 не удается.

+0

Вы можете сделать это еще более succint, используя 'Just n <- readMaybe <$> (string" s: "*> many1 digit)', поскольку сбой шаблона в монаде совпадает с 'mzero'. –

Смежные вопросы