Domanda

Abbiamo appena iniziato a calci le gomme pyparsing e, come finora, ma siamo stati in grado di farlo per aiutarci ad analizzare le stringhe numero frazionario di trasformarli in tipi di dati numerici.

Ad esempio, se un valore di colonna in una tabella del database conteneva la stringa:

1 1/2

Vorremmo qualche modo per convertirlo in pitone numerica equivalente:

1.5

Ci piacerebbe fare un parser che non importa se i numeri nella frazione sono interi o reali. Per esempio, vorremmo:

1.0 1.0 / 2.0

... da tradurre ancora:

1.5

In sostanza vorremmo un parser concettualmente per fare quanto segue:

"1 1/2" = 1 + 0,5 = 1,5

Il seguente codice di esempio sembra farci chiuso ...

http://pyparsing.wikispaces.com/file/view/parsePythonValue.py

... ma non abbastanza vicino per farsi strada. Tutti i nostri test per fare un gestore di numero frazionario tornare solo la prima parte dell'espressione (1). Suggerimenti? Suggerimenti? Saggezza tempestivo? :)

È stato utile?

Soluzione

Dal momento che si citano alcuni test, sembra che tu abbia almeno preso una pugnalata al problema. Presumo che hai già definito un singolo numero, che può essere un numero intero o reale - non importa, si sta convertendo tutto a stare a galla in ogni caso - e di una frazione di due numeri, probabilmente qualcosa di simile:

from pyparsing import Regex, Optional

number = Regex(r"\d+(\.\d*)?").setParseAction(lambda t: float(t[0]))

fraction = number("numerator") + "/" + number("denominator")
fraction.setParseAction(lambda t: t.numerator / t.denominator)

(Si noti l'uso di azioni di parsing, che fanno la conversione in virgola mobile e frazionale divisione a destra in fase di analisi. Io preferisco fare questo durante l'analisi, quando so qualcosa è un numero o di una frazione o qualsiasi altra cosa, invece di tornare più tardi e spulciando tra una serie di stringhe frammentati, cercando di ricreare la logica di riconoscimento che il parser ha già fatto).

Qui ci sono i casi di test ho composto per il vostro problema, costituito da un numero intero, una frazione, e un numero intero e una frazione, usando entrambi i numeri interi e reali:

tests = """\
1
1.0
1/2
1.0/2.0
1 1/2
1.0 1/2
1.0 1.0/2.0""".splitlines()

for t in tests:
    print t, fractExpr.parseString(t)

L'ultimo passo è come definire un'espressione frazionata che può essere un numero singolo, una frazione, o un singolo numero e una frazione.

Dal pyparsing è da sinistra a destra, non fa lo stesso tipo di backtracking come regexen fare. Quindi, questa espressione non funzionerà così bene:

fractExpr = Optional(number) + Optional(fraction)

Per riassumere insieme i valori numerici che potrebbero venire dal numero e frazione parti, aggiungere questa azione di analisi:

fractExpr.setParseAction(lambda t: sum(t))

I nostri test stampare:

1 [1.0]
1.0 [1.0]
1/2 [1.0]
1.0/2.0 [1.0]
1 1/2 [1.5]
1.0 1/2 [1.5]
1.0 1.0/2.0 [1.5]

Per il caso 1/2 di test, che contiene solo una frazione di per sé, il numeratore che porta corrisponde al termine Optional(number), ma che ci lascia solo con "/ 2", che non abbinare il Optional(fraction) - per fortuna, dal momento che il secondo termine è facoltativo, questo "passa", ma non è davvero fare quello che vogliamo.

Abbiamo bisogno di fare fractExpr un po 'più intelligente, e hanno lo sguardo prima per una frazione solitario, poiché non v'è questo potenziale confusione tra un numero solitario e il numeratore leader di una frazione. Il modo più semplice per farlo è quello di rendere fractExpr leggere:

fractExpr = fraction | number + Optional(fraction)

Ora, con questo cambiamento, i nostri test escono meglio:

1 [1.0]
1.0 [1.0]
1/2 [0.5]
1.0/2.0 [0.5]
1 1/2 [1.5]
1.0 1/2 [1.5]
1.0 1.0/2.0 [1.5]

Ci sono un paio di insidie ??classici con pyparsing, e questo è uno di loro. Basta ricordare che pyparsing fa solo il lookahead che gli si dice di, altrimenti è solo dritto da sinistra a destra di analisi.

Altri suggerimenti

Non è esattamente quello che stai cercando, ma ...

>>> import fractions
>>> txt= "1 1/2"
>>> sum( map( fractions.Fraction, txt.split() ) )
Fraction(3, 2)
>>> float(_)
1.5

Questa ricetta può essere utile:

Guardatevi intorno linea 39:

mixed = Combine(numeral + fraction, adjacent=False, joinString=' ')

Questa è una specie di doppia con S. Lott, ma qui è lo stesso:

from fractions import Fraction
print sum(Fraction(part) for part in '1 1/2'.split())

Si occupano di galleggiare 'interi' sarebbe abbastanza contorto, però:

from fractions import Fraction
clean = '1.0 1.0/2.0'.replace('.0 ',' ').replace('.0/', '/').rstrip('0.').split()
print(clean)
print(sum(Fraction(part) for part in clean))

E altri esempi del manifesto, più uno con / con spazi:

from fractions import Fraction

tests = """\
1
1.0
1/2
1.0/2.0
1 1/2
1.0 1/2
1.0 1.0/2.0
1.0 1.0 / 2.0
""".splitlines()

for t in tests:
    clean = t.replace('.0 ',' ').replace('.0/', '/').rstrip('0.').split()
    value = sum(Fraction(part) for part in clean)
    print('%s -> %s, %s = %f' % (t, clean, value, float(value)))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top