2015-04-09 1 views
1

Je suis nouveau au Parsec. Apprécierez les pointeurs pour le problème ici. Dites, j'ai un fichier csv avec un nombre fixe d'en-têtes. Au lieu d'analyser chaque ligne séparément, je voudrais chercher un jeton au début de la ligne, et obtenir toutes les lignes jusqu'à la ligne suivante avec un jeton non vide. Exemple ci-dessous:Comment analyser plusieurs lignes avec des jetons de début et de fin dans Parsec

token,flag,values 
a,1, 
,,a 
,,f 
b,2, 

Règle pour une entrée valide est: si le champ jeton est rempli, obtenir toutes les lignes jusqu'à champ suivant jeton non vide. Alors, je vais vous Parsec pour obtenir plusieurs lignes ci-dessous comme première entrée (ces multiples lignes peuvent ensuite être analysées par une autre règle):

a,1, 
,,a 
,,f 

Ensuite, le processus recommence sur la ligne suivante avec le champ symbolique non vide (dernière ligne dans l'exemple ici). Ce que j'essaie de comprendre, c'est s'il existe un moyen simple de spécifier la règle comme cela dans Parsec - obtenir toutes les lignes qui répondent à une certaine règle. Ils pourraient ensuite être remis à un autre analyseur. Fondamentalement, il ressemble à une sorte de règle lookahead pour spécifier ce qui est une entrée multi-ligne valide. Ai-je bien compris? Nous pouvons ignorer le séparateur de virgules ci-dessus pour le moment, et dire qu'une entrée commence lorsqu'un caractère est trouvé au début d'une ligne, et se termine lorsqu'un caractère est trouvé au début d'une ligne.

+2

Il semble que vous pourriez juste faire quelque chose comme 'nonEmptyTokenField *> many1 emptyTokenField'. – user2407038

+0

@ user2407038, merci pour le pointeur. C'était très utile pour résoudre le problème. Simple et efficace. Je posterai une solution que j'ai écrite avec votre pointeur. – Sal

Répondre

2

J'ai résolu le problème avec l'aide de @ user2407038 qui a suggéré le contour de base dans un commentaire. Solution et explication ci-dessous (s'il vous plaît voir les commentaires après la fonction - ils montrent comment la fonction se comporte avec l'entrée):

{-# LANGUAGE FlexibleContexts #-} 
import Control.Monad 
import Text.Parsec 
import Control.Applicative hiding ((<|>), many) 


-- | this one accepts everything until newline, and discards the newline 
-- | This one is used as building block in the functions below 
restOfLine :: Stream s m Char => ParsecT s u m [Char] 
restOfLine = many1 (satisfy (\x -> not $ x == '\n')) <* char '\n' 

-- | a line with token is "many alphanumeric characters" followed by 
-- | any characters until newline 
tokenLine :: Stream s m Char => ParsecT s u m [Char] 
tokenLine = (++) <$> many1 alphaNum <*> restOfLine 

-- | ghci test: 
-- | *Main Text.Parsec> parseTest tokenLine "a,1,,\n" 
-- | "a,1,," 
-- | *Main Text.Parsec> parseTest tokenLine ",1,,\n" 
-- | parse error at (line 1, column 1): 
-- | unexpected "," 
-- |expecting letter or digit 

-- | a non-token line is a line that has any number of spaces followed 
-- | by ",", then any characters until newline 
nonTokenLine :: Stream s m Char => ParsecT s u m [Char] 
nonTokenLine = (++) <$> (many space) <*> ((:) <$> char ',' <*> restOfLine) 

-- | ghci test: 
-- | *Main Text.Parsec> parseTest nonTokenLine ",1,,\n" 
-- | ",1,," 
-- | *Main Text.Parsec> parseTest nonTokenLine "a,1,,\n" 
-- | parse error at (line 1, column 1): 
-- | unexpected "a" 
-- | expecting space or "," 

-- | One entry is tokenLine followed by any number of nonTokenLine 
oneEntry :: Stream s m Char => ParsecT s u m [[Char]] 
oneEntry = (:) <$> tokenLine <*> (many nonTokenLine) 

-- | ghci test - please note that it drops last line as expected 
-- | *Main Text.Parsec> parseTest oneEntry "a,1,,\n,,a\n,,f\nb,2,,\n" 
-- | ["a,1,,",",,a",",,f"] 


-- | We add 'many' to oneEntry to parse the entire file, and get multiple match entries 
multiEntries :: Stream s m Char => ParsecT s u m [[String]] 
multiEntries = many oneEntry 

-- | ghci test - please note that it gets two entries as expected 
-- | *Main Text.Parsec> parseTest multiEntries "a,1,,\n,,a\n,,f\nb,2,,\n" 
-- | [["a,1,,",",,a",",,f"],["b,2,,"]] 

L'erreur de l'analyseur vu dans les commentaires est prévu sur les entrées non valides. Cela peut être facilement géré. Le code ci-dessus est juste un élément de base pour commencer.