Come scrivere la grammatica per questo in pyparsing: abbina un insieme di parole ma non contiene un dato schema
Domanda
Sono nuovo di Python e pyparsing. Devo realizzare quanto segue.
La mia riga di testo di esempio è così:
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009
Devo estrarre la descrizione dell'oggetto, punto
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
Come farlo?
Soluzione
Suggerirei di considerare SkipTo come la classe di pyparsing più appropriata, dato che hai una buona definizione del testo indesiderato , ma accetterò praticamente qualsiasi cosa prima. Ecco un paio di modi per usare 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
Entrambi i cicli per
stampano quanto segue:
12 items - Ironing Service
Washing service (3 Shirt)
Altri suggerimenti
M K Saravanan, questo particolare problema di analisi non è così difficile da fare con un buon 'ole re:
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
rendimenti
# ('12 items - Ironing Service', '11 Mar 2009 to 10 Apr 2009')
# ('Washing service (3 Shirt)', '23 Mar 2009')
Il cavallo di battaglia principale qui è ovviamente il modello di riferimento. Dividiamolo:
\ d {1,2} \ s + [a-zA-Z] {3} \ s + \ d {4}
è la regexp per una data, l'equivalente di tok_date_in_ddmmmyyyy
. \ d {1,2}
corrisponde a una o due cifre, \ s +
corrisponde a uno o più spazi bianchi, [a-zA-Z] {3} corrisponde a 3 lettere, ecc.
(?: \ s + to \ s + \ d {1,2} \ s + [a-zA-Z] {3} \ s + \ d {4})?
è una regexp circondato da (?: ...)
.
Ciò indica una regexp non raggruppante. Usando questo, nessun gruppo (ad esempio match.group (2)) è assegnato a questo regexp. Ciò è importante perché date_pat.split () restituisce un elenco con ciascun gruppo come membro dell'elenco. Sopprimendo il raggruppamento, teniamo insieme l'intero periodo dall'11 marzo 2009 al 10 aprile 2009
. Il punto interrogativo alla fine indica che questo schema può verificarsi zero o una volta. Ciò consente a regexp di abbinare entrambi
23 mar 2009
e dall'11 mar 2009 al 10 aprile 2009
.
text.splitlines ()
divide il testo su \ n
.
date_pat.split ('12 articoli - Servizio stiratura dall'11 marzo 2009 al 10 aprile 2009 ')
divide la stringa nel regexp date_pat. La partita è inclusa nell'elenco restituito. Quindi otteniamo:
['12 articoli - Servizio stireria ', '11 marzo 2009-10 aprile 2009', '']
map (string.strip, date_pat.split (line) [: 2])
imposta il risultato in modo predefinito.
Se line
non corrisponde a date_pat
, date_pat.split (line)
restituisce [line,]
,
così
descrizione, periodo = map (string.strip, date_pat.split (linea) [: 2])
genera un ValueError perché non è possibile decomprimere un elenco con un solo elemento in una 2-tupla. Prendiamo questa eccezione ma passiamo semplicemente alla riga successiva.