2012-08-28 13 views
7

Ho una domanda riguardante la valutazione dell'espressione matematica all'interno di una stringa. Per esempio la mia stringa è la seguente:Python - Valuta l'espressione matematica all'interno della stringa

my_str='I have 6 * (2 + 3) apples' 

mi chiedo come valutare questa stringa e ottenere il seguente risultato:

'I have 30 apples' 

È il modo per fare questo?

Grazie in anticipo.

P.S. la funzione eval di python non è di aiuto in questo caso. Ha generato un errore durante il tentativo di valutare con la funzione eval.

+3

È questo compito? –

+3

http://stackoverflow.com/questions/2371436/evaluating-a-mathematical-expression-in-a-string –

+1

@jeffery_the_wind non proprio sul punto, in quanto ciò (a differenza di questo) richiede l'eliminazione delle parti non matematiche della stringa. –

risposta

0

Questo è un problema molto difficile che è probabilmente quasi impossibile da risolvere in generale. Tuttavia, ecco un modo semplice per attaccare il problema che funziona con l'input di esempio.

* passaggio 1: disinfettare l'input. Questa è la parte più difficile da fare in generale. Fondamentalmente, hai bisogno di un modo per estrarre una singola espressione matematica dalla stringa senza ridurla. Ecco una semplice espressione regolare lavorerà:

sanitized = re.sub(r'[a-zA-Z]','',my_str).strip() 

* Fase 2 - Valutare con eval:

value = eval(sanitized, {'__builtins__':None}) 

* Fase 3 - indietro sostituto

new_string = my_str.replace(sanitized, str(value)) 
+0

Beh, non è che non può essere risolto in generale. È un problema mal definito così com'è (cosa costituisce un'espressione da valutare e cosa no?) Ma una volta che lo inchiodi, è facilmente risolvibile, se in realtà ti preoccupi di analizzare le cose invece di abusare di "eval". – delnan

+1

@delnan - se questo è l'abuso di "eval", allora cosa non è abuso di "eval" (a quel punto, non dovrebbe essere rimosso completamente dalla lingua?). Penso che questo sia un posto perfetto per usare 'eval' dato che il problema è limitato abbastanza per analizzare l'espressione da valutare dalla stringa di input. – mgilson

+0

Io per prima cosa, "eval" non dovrebbe essere messo in evidenza (cioè nello spazio dei nomi globale). Conosco use case per 'compile' e' exec' (e sono molto diversi da questo, in quanto controllano la stringa di input al 100% ed è noto cosa farà). Devo ancora imbattersi in un caso di uso corretto di "eval": quando vuoi valutare un'espressione matematica, scrivi un valutatore di smistamento o qualcosa del genere. Se vuoi eseguire il codice Python, usa 'exec' perché è meno limitato. Non evito 'eval' per la correttezza, ma non confondere il codice con i dati. (Avvita i ragazzi più chiari che dicono che il codice è dati.) – delnan

2

Ecco il mio tentativo:

>>> import string 
>>> s = 'I have 6 * (2+3) apples' 
>>> symbols = '^*()/+-' 
>>> formula = [(x,s.index(x)) for x in s if x in string.digits+symbols] 
>>> result = eval(''.join(x[0] for x in formula), {'__builtins__':None}) 
>>> s = s[:formula[0][1]] + str(result) + s[formula[-1][1]+1:] 
>>> s 
'I have 30 apples' 

Note:

Questo è molto semplice, non si occuperà di equazioni complesse - come quelle con radice quadrata, pi, ecc. Ma credo che sia nello spirito di ciò che la domanda è dopo. Per una veramente risposta valida vedere question posted by jeffery_the_wind; ma credo che potrebbe essere eccessivo per questo caso semplicistico.

0

Per una soluzione senza l'utilizzo di eval, ecco cosa farei. Comincia a trovare tutte le espressioni matematiche nella stringa, che io definirei una stringa che contiene spazi, parentesi, numeri e operazioni, poi striscia fuori i fiammiferi che sono tutti gli spazi:

>>> import re 
>>> my_str = 'I have 6 * (2 + 3) apples' 
>>> exprs = list(re.finditer(r"[\d\.\s\*\+\-\/\(\)]+", my_str)) 
>>> exprs = [e for e in exprs if len(my_str[e.start():e.end()].strip()) > 0] 

Avanti, valutare le espressioni utilizzando la classe NumericStringParser da this question, che utilizza pyparsing:

>>> nsp = NumericStringParser() 
>>> results = [nsp.eval(my_str[e.start():e.end()]) for e in exprs] 
>>> results 
[30.0] 

Poi, per sostituire i risultati indietro nell'espressione, invertire ordinare le espressioni dal loro indice iniziale e metterli indietro nella stringa originale:

>>> new_str = my_str 
>>> for expr, res in sorted(zip(exprs, results), key=lambda t: t[0].start(), reverse=True): 
...  new_str = new_str[:expr.start()] + (" %d " % res) + new_str[expr.end():] 
... 
>>> new_str 
'I have 30 apples' 
2

A volte è meglio semplificare la domanda anziché elaborare soluzioni complicate.Si consiglia di semplificare il problema di avere il codice essere fornito come questo

my_str='I have {6 * (2 + 3)} apples' 

In questo modo è possibile analizzare utilizzando una semplice espressione regolare e eval cosa c'è dentro. Altrimenti sei molto complicato.

0

mia opzione:

>>> import re 
>>> def calc(s): 
...  val = s.group() 
...  if not val.strip(): return val 
...  return " %s " % eval(val.strip(), {'__builtins__': None}) 
>>> re.sub(r"([0-9\ \.\+\*\-\/(\)]+)", calc, "I have 6 * (2 + 3) apples") 
'I have 30 apples' 
1

Grazie a tutti per il vostro aiuto. In realtà il mio esempio fornito è molto semplice rispetto a quello che ho nel compito reale. Ho letto queste stringhe da file e, talvolta, è in grado di avere vista in questo modo:

my_str='ENC M6_finger_VNCAPa (AA SYZE BY (0.14*2)) < (0.12 + 0.07) OPPOSITE REGION' 

equazione matematica sono semplici ma in grado si verifica molte volte in una stringa, e dovrebbero essere valutati separatamente.

Così scrivo un codice di esempio, che è in grado di gestire questi casi: forse non è così buona, ma risolvere il problema:

def eval_math_expressions(filelist): 
     for line in filelist: 
       if re.match('.*[\-|\+|\*|\/].*',line): 
         lindex=int(filelist.index(line)) 
         line_list=line.split() 
         exp=[] 
         for word in line_list: 
           if re.match('^\(+\d+',word) or re.match('^[\)+|\d+|\-|\+|\*|\/]',word): 
             exp.append(word) 
           else: 
             ready=' '.join(exp) 
             if ready: 
               eval_ready=str(eval(ready)) 
               line_new=line.replace(ready,eval_ready) 
               line=line_new 
               filelist[lindex]=line 
             exp=[] 
     return filelist 
0

[So che questa è una vecchia questione, ma vale la pena di sottolineare nuove soluzioni utili come pop up]

Dal python3.6, questa capacità è ora incorporato nella lingua, coniato "F-stringhe".

See: PEP 498 -- Literal String Interpolation

Per esempio (si noti la f prefisso):

f'I have {6 * (2 + 3)} apples' 
=> 'I have 30 apples' 
color = 'green' 
f'I have {6 * (2 + 3)} {color} apples' 
=> 'I have 30 green apples'