2013-01-02 3 views
2

Hier, j'ai posté une question similaire à celle-ci: Python Regex Named Groups. Ce travail est plutôt bien pour des choses simples. Après quelques recherches, j'ai lu sur la bibliothèque de pyparsing qui semble être assez parfait pour mes tâches.Pyparsing séquence de motif répétitif

text = '[@a eee, fff fff, ggg @b eee, fff, ggg @c eee eee, fff fff,ggg [email protected]]' 
command_s = Suppress(Optional('[') + Literal('@')) 
command_e = Suppress(Literal('@') | Literal(']')) 
task = Word(alphas) 
arguments = ZeroOrMore(
    Word(alphas) + 
    Suppress(
     Optional(Literal(',') + White()) | Optional(White() + Literal('@')) 
    ) 
) 
command = Group(OneOrMore(command_s + task + arguments + command_e)) 
print command.parseString(text) 

# which outputs only the first @a sequence 
# [['a', 'eee', 'fff', 'fff', 'ggg']] 

# the structure should be someting like: 
[ 
    ['a', 'eee', 'fff fff', 'ggg'], 
    ['b', 'eee', 'fff', 'ggg'], 
    ['c', 'eee eee', 'fff fff', 'ggg ggg'], 
    ['d'] 
] 

@ indique le début d'une séquence, le premier mot est un groupe (a), suivi par des arguments séparés par des virgules optionnels (eee fff fff,, GGG). Le problème est que @b, @c et @d sont ignorés par le code ci-dessus. Aussi "fff fff" est traité comme deux arguments séparés, il ne devrait en être qu'un.

Répondre

4

Voir les commentaires incorporés.

text = '[@a eee, fff fff, ggg @b eee, fff, ggg @c eee eee, fff fff,ggg [email protected]]' 

from pyparsing import * 

LBRACK,RBRACK,AT = map(Suppress,"[]@") 

key = AT + Word(alphas) 

# use originalTextFor to preserve whitespace between words between commas 
list_item = originalTextFor(OneOrMore(Word(alphas))) 

# define a key_value pair using Group to preserve structure 
key_value = Group(key + Optional(delimitedList(list_item))) 

parser = LBRACK + OneOrMore(key_value) + RBRACK 
print parser.parseString(text) 

Ceci affichera la sortie désirée.

[['a', 'eee', 'fff fff', 'ggg'], 
['b', 'eee', 'fff', 'ggg'], 
['c', 'eee eee', 'fff fff', 'ggg ggg'], 
['d']] 

Pour un crédit supplémentaire, voici comment avoir pyparsing définir les clés pour vous:

# Extra credit: 
# use Dict to auto-define named groups using each '@x' as a key 
parser = LBRACK + Dict(OneOrMore(key_value)) + RBRACK 
result = parser.parseString(text) 

# print the parsed keys 
print result.keys() 

# print a value for a particular key 
print result['c'] 

# print a value for a particular key using object notation 
print result.b 

# dump out the whole structure to see just what we got 
print result.dump() 

Prints

['a', 'c', 'b', 'd'] 
['eee eee', 'fff fff', 'ggg ggg'] 
['eee', 'fff', 'ggg'] 
[['a', 'eee', 'fff fff', 'ggg'], ['b', 'eee', 'fff', 'ggg'], ['c', 'eee eee', 'fff fff', 'ggg ggg'], ['d']] 
- a: ['eee', 'fff fff', 'ggg'] 
- b: ['eee', 'fff', 'ggg'] 
- c: ['eee eee', 'fff fff', 'ggg ggg'] 
- d: 
+0

Jésus-Christ, doivent vérifier. Merci pour l'instant! – hetsch

+0

Fonctionne parfaitement !!! Je n'ai aucune idée de comment ça marche sous le capot - il faut creuser et utiliser les docs de l'API. Billiant, merci beaucoup. – hetsch

+0

Regardez la seule différence entre les deux définitions de l'analyseur, la deuxième enveloppe le 'OneOrMore (key_value)' avec 'Dict'. Les listes analysées sont les mêmes, mais 'Dict' crée des noms de résultats (comme les groupes nommés de regex) en utilisant le premier élément de chaque groupe comme clé, et le reste de chaque groupe comme valeur. Si vous jetez un coup d'oeil dans le code de pyparsing, ceci est fait dans 'Dict.postParse'. – PaulMcG