Domanda

Pythonisti:

Si supponga di voler analizzare la stringa seguente utilizzando Pyparsing:

'ABC_123_SPEED_X 123'

erano ABC_123 è un identificatore; SPEED_X è un parametro, e 123 è un valore. Pensai alla seguente BNF utilizzando Pyparsing:

Identifier = Word( alphanums + '_' )
Parameter = Keyword('SPEED_X') or Keyword('SPEED_Y') or Keyword('SPEED_Z')
Value = # assume I already have an expression valid for any value
Entry = Identifier + Literal('_') + Parameter + Value
tokens = Entry.parseString('ABC_123_SPEED_X 123')
#Error: pyparsing.ParseException: Expected "_" (at char 16), (line:1, col:17)

Se rimuovo il carattere di sottolineatura dalla metà (e regolare la definizione Entry di conseguenza) che analizza in modo corretto.

Come posso fare questo parser essere un po 'più pigri e attendere fino a che non corrisponde alla parola chiave (al contrario di slurping l'intera stringa come un identificatore e in attesa del _, che non esiste.

Grazie.

[Nota: Questa è una completa riscrittura della mia domanda; Non avevo capito qual è il vero problema era]

È stato utile?

Soluzione

Ho basato la mia risposta fuori di questo , dal momento che ciò che si' stiamo cercando di fare è ottenere un match non-greedy. Sembra che questo è difficile da far accadere in pyparsing, ma non impossibile con una certa intelligenza e il compromesso. Quanto segue sembra funzionare:

from pyparsing import *
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
UndParam = Suppress('_') + Parameter
Identifier = SkipTo(UndParam)
Value = Word(nums)
Entry = Identifier + UndParam + Value

Quando si esegue questa dall'interprete interattivo, possiamo vedere il seguente:

>>> Entry.parseString('ABC_123_SPEED_X 123')
(['ABC_123', 'SPEED_X', '123'], {})

Si noti che questo è un compromesso; perché io uso SkipTo, il Identifier può essere piena di personaggi disgustosi, il male, non solo bella alphanums con la sottolineatura occasionale.

Modifica Grazie a Paul McGuire, siamo in grado di inventare una soluzione davvero elegante impostando Identifier al seguente:

Identifier = Combine(Word(alphanums) +
        ZeroOrMore('_' + ~Parameter + Word(alphanums)))

Diamo ispezionare come funziona. In primo luogo, ignorare il Combine esterno; ci arriveremo più avanti. A partire da Word(alphanums) sappiamo che otterremo la parte 'ABC' della stringa di riferimento, 'ABC_123_SPEED_X 123'. E 'importante notare che non abbiamo permettiamo la "parola" per contenere underscore in questo caso. Costruiamo che separatamente alla logica.

Quindi, abbiamo bisogno di catturare la parte '_123' senza succhiare anche in '_SPEED_X'. Saltiamo anche oltre ZeroOrMore a questo punto e tornare in un secondo momento. Si comincia con il trattino basso come Literal, ma siamo in grado di scorciatoia con un solo '_', che ci ottenere la sottolineatura, ma non tutti '_123'. Instictively, ci sarebbe posto un'altra Word(alphanums) per catturare il resto, ma questo è esattamente ciò che ci porterà nei guai consumando tutto il '_123_SPEED_X' rimanente. Invece, diciamo: "Fino a quando ciò che segue la sottolineatura è non il Parameter, analizziamo che, come parte del mio Identifier. Affermiamo che in pyparsing termini come '_' + ~Parameter + Word(alphanums). Dal momento che diamo per scontato che possiamo avere un arbitrario numero di sottolineatura + WordButNotParameter ripete, abbiamo avvolgere quell'espressione un costrutto ZeroOrMore. (Se vi aspettate sempre almeno sottolineare + WordButNotParameter dopo l'iniziale, è possibile utilizzare OneOrMore.)

Infine, abbiamo bisogno di avvolgere la parola iniziale e la sottolineatura speciale + Word ripete insieme in modo che si capisce che sono contigui, non separati da spazi, in modo da avvolgere l'intera espressione in un costrutto Combine. In questo modo 'ABC _123_SPEED_X' alzerà un errore di analisi, ma 'ABC_123_SPEED_X' sarà analizzare correttamente.

Si noti anche che ho dovuto cambiare Keyword per Literal perché le vie della ex sono troppo sottili e veloce da rabbia. Non mi fido Keywords, nè potrei avere corrispondenza con loro.

Altri suggerimenti

Se si è certi che l'identificatore non finisce mai con una sottolineatura, è possibile farla rispettare nella definizione:

from pyparsing import *

my_string = 'ABC_123_SPEED_X 123'

Identifier = Combine(Word(alphanums) + Literal('_') + Word(alphanums))
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
Value = Word(nums)
Entry = Identifier + Literal('_').suppress() + Parameter  + Value
tokens = Entry.parseString(my_string)

print tokens # prints: ['ABC_123', 'SPEED_X', '123']

Se non è il caso, ma se la lunghezza identificatore è fissa è possibile definire Identifier in questo modo:

Identifier = Word( alphanums + '_' , exact=7)

È anche possibile analizzare l'identificatore e il parametro come un gettone, e dividerli in un'azione di analisi:

from pyparsing import *
import re

def split_ident_and_param(tokens):
    mo = re.match(r"^(.*?_.*?)_(.*?_.*?)$", tokens[0])
    return [mo.group(1), mo.group(2)]

ident_and_param = Word(alphanums + "_").setParseAction(split_ident_and_param)
value = Word(nums)
entry = ident_and_param + value

print entry.parseString("APC_123_SPEED_X 123")

L'esempio sopra presuppone che gli identificatori e parametri hanno sempre la XXX_YYY formato (contenente un singolo trattino).

Se questo non è il caso, è necessario regolare il metodo split_ident_and_param().

Questo risponde a una domanda che probabilmente hanno anche chiesti: "Che cosa è un mondo reale applicazione per reduce):

>>> keys = ['CAT', 'DOG', 'HORSE', 'DEER', 'RHINOCEROS']
>>> p = reduce(lambda x, y: x | y, [Keyword(x) for x in keys])
>>> p
{{{{"CAT" | "DOG"} | "HORSE"} | "DEER"} | "RHINOCEROS"}

Modifica

Questa è stata una buona risposta alla domanda iniziale. Dovrò lavorare su quello nuovo.

Ulteriori edit:

Sono abbastanza sicuro che non si può fare quello che stai cercando di fare. Il parser che pyparsing crea non fa lookahead. Quindi, se gli si dice di abbinare Word(alphanums + '_'), sta andando a mantenere i caratteri corrispondenti finché non si trova uno che non è una lettera, un numero, o di sottolineatura.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top