2017-08-17 5 views
2

Je n'arrive pas à comprendre comment regrouper zéro ou plusieurs sections répétées dans le texte avec pyparsing. En d'autres termes, je souhaite fusionner plusieurs correspondances dans un jeu de résultats nommé. Remarque, je veux utiliser Pyparsing car j'ai beaucoup de sections avec des règles différentes.Grouper plusieurs sections (correspondances) en utilisant Pyparsing

from pyparsing import *  

input_text = """ 
Projects 
project a created in c# 

Education 
university of college 

Projects 
project b created in python 
""" 

project_marker = LineStart() + Literal('Projects') + LineEnd() 
education_marker = LineStart() + Literal('Education') + LineEnd() 
markers = project_marker^education_marker 

project_section = Group(
    project_marker + SkipTo(markers | stringEnd).setResultsName('project') 
).setResultsName('projects') 
education_section = Group(
    education_marker + SkipTo(markers | stringEnd).setResultsName('education') 
).setResultsName('educations') 
sections = project_section^education_section 

text = StringStart() + SkipTo(sections | StringEnd()) 
doc = Optional(text) + ZeroOrMore(sections) 
result = doc.parseString(input_text) 

print(result) 
# ['', ['Projects', '\n', 'project a created in c#'], ['Education', '\n', 'virginia tech'], ['Projects', '\n', 'project b created in python']] 
print(result.projects) 
# ['Projects', '\n', 'project b created in python'] 
print(result.projects[0].project) 
# AttributeError: 'str' object has no attribute 'project' 
+0

Une section "Projets" peut-elle contenir plus d'une ligne décrivant un projet? Et de même pour les sections 'Education'? –

+1

Dans vos appels à 'setResultsName' sur project_section et education_section, ajoutez' listAllMatches = True'. Ensuite, je pense que votre code fonctionnera tel quel. – PaulMcG

+0

@PaulMcG Ajouter 'listAllMatches = True' regroupe les résultats, merci! –

Répondre

0

Merci à @PaulMcG la solution est d'ajouter listAllMatches=True-setResultsName, voir https://pythonhosted.org/pyparsing/pyparsing.ParserElement-class.html#setResultsName.

project_marker = LineStart() + Literal('Projects') + LineEnd() 
education_marker = LineStart() + Literal('Education') + LineEnd() 
markers = project_marker^education_marker 

project_section = Group(
    project_marker + SkipTo(markers | stringEnd).setResultsName('project') 
).setResultsName('projects', listAllMatches=True) 
education_section = Group(
    education_marker + SkipTo(markers | stringEnd).setResultsName('education') 
).setResultsName('educations', listAllMatches=True) 
sections = project_section^education_section 

text = StringStart() + SkipTo(sections | StringEnd()) 
doc = Optional(text) + ZeroOrMore(sections) 
result = doc.parseString(input_text) 
2

Voici ma réponse provisoire, pas que j'en sois fier. J'ai criblé un morceau de https://stackoverflow.com/a/5824309/131187.

>>> import pyparsing as pp 
>>> pp.ParserElement.setDefaultWhitespaceChars(" \t") 
>>> EOL = pp.LineEnd().suppress() 
>>> keyword = pp.Or([pp.Keyword('Projects'), pp.Keyword('Education')]) 
>>> line = pp.LineStart() + pp.NotAny(keyword) + pp.SkipTo(pp.LineEnd(), failOn=pp.LineStart()+pp.LineEnd()) + EOL 
>>> lines = pp.OneOrMore(line) 
>>> section = pp.Or([pp.Keyword('Projects'), pp.Keyword('Education')])('section') + EOL + lines('lines') 
>>> sections = pp.OneOrMore(section) 
>>> r = sections.parseString(input_text) 

Comme vous pouvez le voir juste en dessous de cette phrase, l'analyseur parvient à recueillir correctement les informations, et dans la collecte de telle manière qu'il puisse être assemblé, comme nous allons le montrer. Cependant, je ne peux pas trouver un moyen d'accéder à tous les résultats de parseString qui sont si clairement disponibles. J'ai appliqué le eval à sa représentation repr. Ayant fait cela, j'ai été en mesure de choisir toutes les pièces et les assigner à un objet semblable à un dict.

Pour être honnête, ce serait plus facile à faire sans pyparsing. Lisez une ligne, notez s'il s'agit d'un mot-clé. Si c'est le cas, souvenez-vous-en. Ensuite, jusqu'à ce que vous lisiez un autre mot-clé, placez toutes les lignes que vous lisez dans un dictionnaire sous le mot-clé le plus récent.

>>> repr(r) 
"(['Projects', 'project 1', 'project 2', 'project 3', '', 'Education', 'institution 1', 'institution 2', 'institution 3', 'institution 4', '', 'Projects', 'assignment 5', 'assignment 8', 'assignment 10', ''], {'lines': [(['project 1', 'project 2', 'project 3', ''], {}), (['institution 1', 'institution 2', 'institution 3', 'institution 4', ''], {}), (['assignment 5', 'assignment 8', 'assignment 10', ''], {})], 'section': ['Projects', 'Education', 'Projects']})" 
>>> evil_r = eval(repr(r)) 
>>> evil_r 
(['Projects', 'project 1', 'project 2', 'project 3', '', 'Education', 'institution 1', 'institution 2', 'institution 3', 'institution 4', '', 'Projects', 'assignment 5', 'assignment 8', 'assignment 10', ''], {'lines': [(['project 1', 'project 2', 'project 3', ''], {}), (['institution 1', 'institution 2', 'institution 3', 'institution 4', ''], {}), (['assignment 5', 'assignment 8', 'assignment 10', ''], {})], 'section': ['Projects', 'Education', 'Projects']}) 
>>> evil_r[1]['lines'] 
[(['project 1', 'project 2', 'project 3', ''], {}), (['institution 1', 'institution 2', 'institution 3', 'institution 4', ''], {}), (['assignment 5', 'assignment 8', 'assignment 10', ''], {})] 
>>> evil_r[1]['section'] 
['Projects', 'Education', 'Projects'] 
>>> from collections import defaultdict 
>>> section_info = defaultdict(list) 
>>> for k, kind in enumerate(evil_r[1]['section']): 
...  section_info[kind].extend(evil_r[1]['lines'][k][0][:-1]) 
>>> for section in section_info: 
...  section, section_info[section] 
...  
('Education', ['institution 1', 'institution 2', 'institution 3', 'institution 4']) 
('Projects', ['project 1', 'project 2', 'project 3', 'assignment 5', 'assignment 8', 'assignment 10']) 

EDIT: Ou vous pouvez le faire. Besoin de rangement. Au moins, il n'utilise rien de peu orthodoxe.

>>> input_text = open('temp.txt').read() 
>>> import pyparsing as pp 
>>> pp.ParserElement.setDefaultWhitespaceChars(" \t") 
>>> from collections import defaultdict 
>>> class Accum: 
...  def __init__(self): 
...   self.current_section = None 
...   self.result = defaultdict(list) 
...  def __call__(self, s): 
...   if s[0] in ['Projects', 'Education']: 
...    self.current_section = s[0] 
...   else: 
...    self.result[self.current_section].extend(s[:-1]) 
... 
>>> accum = Accum() 
>>> EOL = pp.LineEnd().suppress() 
>>> keyword = pp.Or([pp.Keyword('Projects'), pp.Keyword('Education')]) 
>>> line = pp.LineStart() + pp.NotAny(keyword) + pp.SkipTo(pp.LineEnd(), failOn=pp.LineStart()+pp.LineEnd()) + EOL 
>>> lines = pp.OneOrMore(line) 
>>> section = pp.Or([pp.Keyword('Projects'), pp.Keyword('Education')]).setParseAction(accum) + EOL + lines.setParseAction(accum) 
>>> sections = pp.OneOrMore(section) 
>>> r = sections.parseString(input_text) 
>>> accum.result['Education'] 
['institution 1', 'institution 2', 'institution 3', 'institution 4'] 
>>> accum.result['Projects'] 
['project 1', 'project 2', 'project 3', 'assignment 5', 'assignment 8', 'assignment 10'] 
+0

Une autre approche ajoutée. –

+0

Veuillez utiliser le '|' ou '^' pour combiner des expressions alternatives, ce qui nettoie un peu la grammaire: 'keyword = pp.Keyword ('Projects') | pp.Keyword ('Education') '. Pyparsing inclut 'restOfLine' comme un moyen facile de lire tout jusqu'à la prochaine ligne, donc la ligne simplifie à:' line = pp.LineStart() + pp.NotAny (mot-clé) + pp.restOfLine + EOL'. La dernière chose dont vous avez besoin est de regrouper chaque mot-clé et ses lignes associées, en utilisant la classe Group: 'section = pp.Group (mot-clé ('section') + EOL + lignes ('lignes'))'. – PaulMcG

+0

Enfin, imprimez les résultats en utilisant 'print (r.dump())'. Cela montre comment vous pouvez accéder à chaque section analysée comme 'pour section_data dans r: print section_data.section'. Pas besoin de repr et d'eval. En savoir plus sur la classe 'ParseResults' de pyparsing à l'adresse https://pythonhosted.org/pyparsing/pyparsing.ParseResults-class.html. – PaulMcG