Como escrever a gramática para isso em pyparsing: combinar um conjunto de palavras mas não contém um determinado padrão
Pergunta
Eu sou novo para Python e pyparsing. Eu preciso para realizar o seguinte.
A minha linha de amostra do texto é assim:
12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009
Washing service (3 Shirt) 23 Mar 2009
Eu preciso extrair a descrição do item, 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
Como fazer isso?
Solução
Eu sugeriria olhar skipTo como a classe pyparsing que é mais apropriado, uma vez que você tem uma definição bem da indesejado texto, mas vai aceitar praticamente qualquer coisa antes disso. Aqui estão algumas maneiras 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 os laços for
imprimir o seguinte:
12 items - Ironing Service
Washing service (3 Shirt)
Outras dicas
M K Saravanan, este problema de análise em particular não é tão difícil de fazer com um bom 're ole:
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
rendimentos
# ('12 items - Ironing Service', '11 Mar 2009 to 10 Apr 2009')
# ('Washing service (3 Shirt)', '23 Mar 2009')
A principal cavalo de batalha aqui é, naturalmente, o padrão de re. Vamos dividi-la em pedaços:
\d{1,2}\s+[a-zA-Z]{3}\s+\d{4}
é a expressão regular para uma data, o equivalente a tok_date_in_ddmmmyyyy
. \d{1,2}
corresponde a um ou dois dígitos, \s+
corresponde a um ou mais espaços em branco, [a-zA-Z]{3}
corresponde 3 letras, etc.
(?:\s+to\s+\d{1,2}\s+[a-zA-Z]{3}\s+\d{4})?
é um regexp cercado por (?:...)
.
Isto indica uma expressão regular não-agrupamento. Usando este, nenhum grupo (por exemplo match.group (2)) é atribuída a esta expressão regular. Isto é importante porque date_pat.split () retorna uma lista com cada grupo sendo um membro da lista. Ao suprimir o agrupamento, mantemos todo o conjunto período 11 Mar 2009 to 10 Apr 2009
. O ponto de interrogação no final indica que este padrão pode ocorrer zero ou uma vez. Isso permite que o regexp para corresponder tanto
23 Mar 2009
e 11 Mar 2009 to 10 Apr 2009
.
texto text.splitlines()
splits em \n
.
date_pat.split('12 items - Ironing Service 11 Mar 2009 to 10 Apr 2009')
divide a corda no regexp date_pat. A partida está incluído na lista retornada. Assim temos:
['12 items - Ironing Service ', '11 Mar 2009 to 10 Apr 2009', '']
map(string.strip,date_pat.split(line)[:2])
prettifies o resultado.
Se line
não corresponde date_pat
, em seguida, retorna date_pat.split(line)
[line,]
,
assim
description,period=map(string.strip,date_pat.split(line)[:2])
levanta uma ValueError porque não pode descompactar uma lista com apenas um elemento em um 2-tupla. Nós pegar essa exceção, mas simplesmente passar para a próxima linha.