2016-11-01 3 views
2

J'essaye d'apprendre Parsec en analysant une chaîne de date de format "YYYYMMDD", par exemple "20161030". Et ma solution est:Comment analyser une chaîne d'entiers seulement dans une certaine gamme avec Parsec?

date :: Parser (String, String, String) 
date = do 
    year <- replicateM 4 digit 
    month <- replicateM 2 digit 
    day <- replicateM 2 digit 
    return (year, month, day) 

Mais le problème est que « 20161356 » est également une date valide pour mon code. Comment puis-je valider le "MM" entre 1 et 12; et "DD" est entre 1 et 31?

+2

'guard $ mois> = 0 && mois <= 12'? –

+1

'> = 1' peut-être? : D – ThreeFx

+0

Side note 20160229 est valide tandis que 20000229 n'est pas, ne foget pas pour vérifier cela aussi – epsilonhalbe

Répondre

2

Vous pouvez ajouter un guard comme suggéré par Thomas M. Dubuisson:

date :: Parser (String, String, String) 
date = do 
    year <- replicateM 4 digit 
    month <- replicateM 2 digit 
    day <- replicateM 2 digit 
    guard $ read month > 0 && read month <= 12 && read day > 0 && read day <= 31 
    return (year, month, day) 

Cependant, cela se traduit par un mauvais message d'erreur:

λ> parse date "" "20161356" 
Left (line 1, column 9):unknown parse error 

Nous pouvons résoudre ce problème en combinant guard avec <?> pour fournir un meilleur message d'erreur:

date :: Parser (String, String, String) 
date = do 
    year <- replicateM 4 digit 
    month <- replicateM 2 digit 
    guard (read month > 0 && read month <= 12) <?> "valid month (1–12)" 
    day <- replicateM 2 digit 
    guard (read day > 0 && read day <= 31) <?> "valid day (1–31)" 
    return (year, month, day) 

Avec cette approche, vous obtenez un message d'erreur plus utile:

λ> parse date "" "20161356" 
Left (line 1, column 7): 
expecting valid month (1–12) 

Comme une note de côté, je pense qu'il est précieuse pour valider (ou au moins vérification de la santé mentale) la date dans un analyseur-il veille à ce que la validation de la date se compose du reste de votre analyseur et du code de gestion des erreurs. Vous ne pouvez pas oublier de vérifier la date plus tard dans votre code et l'erreur est localisée correctement, ce qui est très utile si vous analysez des documents avec beaucoup de dates.

+2

Et bien sûr, en code réel (à la différence du code pédagogique comme affiché ici), vous devriez utiliser ['fromGregorianValid'] (http://hackage.haskell.org/package/time-1.6.0.1/docs/Data-Time -Calendar.html # v: fromGregorianValid) du vénérable paquet 'time' plutôt que de coder un validateur simple mais faux. –

+0

@DanielWagner: Oui, bien que vous souhaitiez aussi faire vos propres vérifications pour avoir des messages d'erreur plus détaillés. –