Cómo escribir la gramática para esto en pyparsing: empareja un conjunto de palabras pero no contiene un patrón dado
Pregunta
Soy nuevo en Python y pyparsing. Necesito lograr lo siguiente.
Mi línea de muestra de texto es así:
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009
Necesito extraer la descripción del artículo, el período
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
¿Cómo hacer esto?
Solución
Sugeriría que consideres a SkipTo como la clase de reproducción más apropiada, ya que tienes una buena definición del texto no deseado , pero aceptarás casi cualquier cosa antes de eso. Aquí hay un par de maneras de usar 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
Ambos para
imprimen lo siguiente:
12 items - Ironing Service
Washing service (3 Shirt)
Otros consejos
M K Saravanan, este problema de análisis en particular no es tan difícil de hacer con un buen '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
rendimientos
# ('12 items - Ironing Service', '11 Mar 2009 to 10 Apr 2009')
# ('Washing service (3 Shirt)', '23 Mar 2009')
El principal caballo de batalla aquí es, por supuesto, el patrón re. Vamos a separarlo:
\ d {1,2} \ s + [a-zA-Z] {3} \ s + \ d {4}
es la expresión regular para una fecha, el equivalente de tok_date_in_ddmmmyyyy
. \ d {1,2}
coincide con uno o dos dígitos, \ s +
coincide con uno o más espacios en blanco, [a-zA-Z] {3} coincide con 3 letras, etc.
(?: \ s + to \ s + \ d {1,2} \ s + [a-zA-Z] {3} \ s + \ d {4})?
es una expresión regular Rodeado por (?: ...)
.
Esto indica una expresión regular no agrupada. Usando esto, no se asigna ningún grupo (por ejemplo, match.group (2)) a esta expresión regular. Esto es importante porque date_pat.split () devuelve una lista con cada grupo como miembro de la lista. Al suprimir la agrupación, mantenemos todo el período 11 marzo 2009 a 10 abril 2009
juntos. El signo de interrogación al final indica que este patrón puede ocurrir cero o una vez. Esto permite que la expresión regular coincida con ambas
23 de marzo de 2009
y 11 de marzo de 2009 al 10 de abril de 2009
.
text.splitlines ()
divide el texto en \ n
.
date_pat.split ('12 artículos - Servicio de planchado del 11 de marzo de 2009 al 10 de abril de 2009 ')
divide la cadena en la regexp date_pat. La coincidencia se incluye en la lista devuelta. Así obtenemos:
['12 items - Ironing Service ', '11 Mar 2009 to 10 Apr 2009', '']
map (string.strip, date_pat.split (line) [: 2])
simula el resultado.
Si line
no coincide con date_pat
, date_pat.split (line)
devuelve [line,]
,
tan
description,period=map(string.strip,date_pat.split(line)[:2?)
genera un ValueError porque no podemos desempaquetar una lista con solo un elemento en un 2-tuple. Capturamos esta excepción pero simplemente pasamos a la siguiente línea.