domanda pyparsing
-
19-09-2019 - |
Domanda
Questo codice funziona:
from pyparsing import *
zipRE = "\d{5}(?:[-\s]\d{4})?"
fooRE = "^\!\s+.*"
zipcode = Regex( zipRE )
foo = Regex( fooRE )
query = ( zipcode | foo )
tests = [ "80517", "C6H5OH", "90001-3234", "! sfs" ]
for t in tests:
try:
results = query.parseString( t )
print t,"->", results
except ParseException, pe:
print pe
Sono bloccato su due questioni:
1 - Come utilizzare una funzione personalizzata per analizzare un token. Per esempio, se volessi usare un po 'di logica personalizzata invece di una regex per determinare se un numero è un codice postale. Invece di:
zipcode = Regex( zipRE )
forse:
zipcode = MyFunc()
2 - Come faccio a determinare ciò che una stringa analizza TO. "80001" analizza a "codice postale" ma come faccio a determinare questo utilizzando pyparsing? Non sto parsing di una stringa per i suoi contenuti, ma semplicemente per determinare quale tipo di interrogazione che è.
Soluzione
La seconda domanda è facile, quindi dovrò rispondere a questa prima. Modificare query per assegnare i nomi ai risultati le diverse espressioni:
query = ( zipcode("zip") | foo("foo") )
Ora è possibile chiamare getName () sul risultato restituito:
print t,"->", results, results.getName()
Dare:
80517 -> ['80517'] zip
Expected Re:('\\d{5}(?:[-\\s]\\d{4})?') (at char 0), (line:1, col:1)
90001-3234 -> ['90001-3234'] zip
! sfs -> ['! sfs'] foo
Se avete intenzione di utilizzare fooness o zipness del risultato per chiamare un'altra funzione, allora si potrebbe fare questo in fase di analisi ed accompagnato da un'azione di analisi per i tuoi Foo e CAP espressioni:
# enclose zipcodes in '*'s, foos in '#'s
zipcode.setParseAction(lambda t: '*' + t[0] + '*')
foo.setParseAction(lambda t: '#' + t[0] + '#')
query = ( zipcode("zip") | foo("foo") )
Ora dà:
80517 -> ['*80517*'] zip
Expected Re:('\\d{5}(?:[-\\s]\\d{4})?') (at char 0), (line:1, col:1)
90001-3234 -> ['*90001-3234*'] zip
! sfs -> ['#! sfs#'] foo
Per la vostra prima domanda, io non esattamente sapere che tipo di funzione che si intende. Pyparsing fornisce molte più classi di analisi di un semplice Regex (come Word, parola chiave, letterale, CaselessLiteral), e di comporre la vostra parser combinandole con '+', '|', '^', '~', '@' e '*' operatori. Per esempio, se si voleva analizzare per un numero di sicurezza sociale degli Stati Uniti, ma non utilizzare un Regex, è possibile utilizzare:
ssn = Combine(Word(nums,exact=3) + '-' +
Word(nums,exact=2) + '-' + Word(nums,exact=4))
Parola corrisponde per contigui "parole" composte dei caratteri dati nel suo costruttore, Unire concatena i gettoni abbinati in un unico token.
Se si voleva analizzare per un potenziale elenco di tali numeri, delimitato da '/' s, uso:
delimitedList(ssn, '/')
o se ci fossero tra 1 e 3 tali numeri, senza delimters, uso:
ssn * (1,3)
E qualsiasi espressione può avere risultati nomi o analizzare le azioni ad essi connessi, per arricchire ulteriormente i risultati analizzati, o la funzionalità durante l'analisi. Si può anche costruire parser ricorsive, come le liste nidificate di parentesi, espressioni aritmetiche, ecc utilizzando la classe di andata.
Il mio intento quando ho scritto pyparsing era che questa composizione del parser da blocchi di costruzione di base sarebbe la forma primaria per la creazione di un parser. E 'stato solo in una versione successiva che ho aggiunto Regex come (quello che mi era) la valvola di sfogo finale - se le persone non riuscivano a costruire la loro parser, potrebbero ripiegare su formato di espressione regolare, che ha definitivamente dimostrato la sua potenza nel tempo.
In alternativa, come suggerisce un altro manifesto, è possibile aprire la fonte pyparsing, e sottoclasse una delle classi esistenti, o lascia la tua, seguendo la loro struttura. Ecco una classe che sarebbe partita per i caratteri appaiati:
class PairOf(Token):
"""Token for matching words composed of a pair
of characters in a given set.
"""
def __init__( self, chars ):
super(PairOf,self).__init__()
self.pair_chars = set(chars)
def parseImpl( self, instring, loc, doActions=True ):
if (loc < len(instring)-1 and
instring[loc] in self.pair_chars and
instring[loc+1] == instring[loc]):
return loc+2, instring[loc:loc+2]
else:
raise ParseException(instring, loc, "Not at a pair of characters")
In modo che:
punc = r"~!@#$%^&*_-+=|\?/"
parser = OneOrMore(Word(alphas) | PairOf(punc))
print parser.parseString("Does ** this match @@@@ %% the parser?")
si ottiene:
['Does', '**', 'this', 'match', '@@', '@@', '%%', 'the', 'parser']
(Notare l'omissione del trailing singolo '?')
Altri suggerimenti
Si potrebbe utilizzare codice postale e foo separatamente, in modo che si sa che una delle partite di stringa.
zipresults = zipcode.parseString( t )
fooresults = foo.parseString( t )
Non ho il modulo pyparsing
, ma Regex
deve essere una classe, non una funzione.
Che cosa si può fare è sottoclasse da esso e metodi di override, come richiesto per personalizzare il comportamento, quindi utilizzare le sottoclassi, invece.