Comment écrire la grammaire pour cela dans pyparsing: faire correspondre un ensemble de mots mais ne contenant pas un motif donné
Question
Je suis nouveau sur Python et pyparsing. Je dois accomplir les tâches suivantes.
Mon exemple de ligne de texte est le suivant:
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009
Je dois extraire la description de l'article, période
tok_date_in_ddmmmyyyy = Combine(Word(nums,min=1,max=2)+ " " + Word(alphas, exact=3) + " " + Word(nums,exact=4))
tok_period = Combine((tok_date_in_ddmmmyyyy + " to " + tok_date_in_ddmmmyyyy)|tok_date_in_ddmmmyyyy)
tok_desc = Word(alphanums+"-()") but stop before tok_period
Comment faire cela?
La solution
Je suggérerais de regarder SkipTo comme la classe pyparse la plus appropriée, car vous avez une bonne définition du texte non souhaité , mais accepterez à peu près n'importe quoi avant. Voici deux manières d'utiliser SkipTo:
text = """\
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009"""
# using tok_period as defined in the OP
# parse each line separately
for tx in text.splitlines():
print SkipTo(tok_period).parseString(tx)[0]
# or have pyparsing search through the whole input string using searchString
for [[td,_]] in SkipTo(tok_period,include=True).searchString(text):
print td
Les deux boucles pour
affichent les éléments suivants:
12 items - Ironing Service
Washing service (3 Shirt)
Autres conseils
M K Saravanan, ce problème d’analyse syntaxique n’est pas si difficile à résoudre avec du bien
import re
import string
text='''
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009
This line does not match
'''
date_pat=re.compile(
r'(\d{1,2}\s+[a-zA-Z]{3}\s+\d{4}(?:\s+to\s+\d{1,2}\s+[a-zA-Z]{3}\s+\d{4})?)')
for line in text.splitlines():
if line:
try:
description,period=map(string.strip,date_pat.split(line)[:2])
print((description,period))
except ValueError:
# The line does not match
pass
donne
# ('12 items - Ironing Service', '11 Mar 2009 to 10 Apr 2009')
# ('Washing service (3 Shirt)', '23 Mar 2009')
Le principal bourreau de travail ici est bien sûr le motif récurrent. Brisons-le:
\ d {1,2} \ s + [a-zA-Z] {3} \ s + \ d {4}
est l'expression rationnelle pour une date, l'équivalent de tok_date_in_ddmmmyyyy
. \ d {1,2}
correspond à un ou deux chiffres, \ s +
correspond à un ou plusieurs espaces, [a-zA-Z] {3}
correspond à 3 lettres, etc.
(?: \ s + à \ s + \ d {1,2} \ s + [a-zA-Z] {3} \ s + \ d {4})?
est une expression rationnelle entouré de (?: ...)
.
Ceci indique une expression rationnelle non-groupante. En utilisant cela, aucun groupe (par exemple match.group (2)) n’est affecté à cette expression rationnelle. Ceci est important car date_pat.split () retourne une liste avec chaque groupe étant membre de la liste. En supprimant le regroupement, nous conservons la période entière du 11 mars 2009 au 10 avril 2009
. Le point d'interrogation à la fin indique que ce motif peut se produire zéro ou une fois. Cela permet à l'expression rationnelle de correspondre à la fois
23 mars 2009
et du 11 mars 2009 au 10 avril 2009
.
text.splitlines ()
divise le texte sur \ n
.
date_pat.split ('12 éléments - Service de repassage du 11 mars 2009 au 10 avril 2009 ')
divise la chaîne sur l'expression rationnelle date_pat. La correspondance est incluse dans la liste renvoyée. Nous obtenons ainsi:
['12 éléments - Service de repassage', 'du 11 mars 2009 au 10 avril 2009', '']
map (string.strip, date_pat.split (line) [: 2])
rend le résultat agréable.
Si ligne
ne correspond pas à date_pat
, date_pat.split (ligne)
renvoie [ligne,]
. ,
alors
description, period = map (string.strip, date_pat.split (line) [: 2])
déclenche une ValueError car nous ne pouvons pas décompresser une liste avec un seul élément dans un 2 -uple. Nous attrapons cette exception mais passons simplement à la ligne suivante.