2012-08-28 113 views
7

Tengo una pregunta sobre la evaluación de la expresión matemática dentro de una cadena. Por ejemplo, mi cadena es el siguiente:Python - Evaluar la expresión matemática dentro de la cadena

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

Me pregunto cómo evaluar esta cadena y obtener el siguiente resultado:

'I have 30 apples' 

¿El ninguna manera de hacer esto?

Gracias de antemano.

P.S. La función eval de Python no ayuda en este caso. Se produjo un error al intentar evaluar con la función eval.

+3

Es esta tarea? –

+3

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

+1

@jeffery_the_wind no está realmente en el punto, ya que esto (a diferencia de eso) requiere descartar partes de la cadena que no sean matemáticas. –

Respuesta

0

Este es un problema muy complicado que probablemente sea casi imposible de resolver en general. Sin embargo, aquí hay una forma sencilla de atacar el problema que funciona con la entrada de ejemplo.

* paso 1 - esterilizar la entrada. Esta es la parte más difícil de hacer en general. Básicamente, necesitas una forma de extraer una sola expresión matemática de la cadena sin modificarla. Aquí una expresión regular sencilla funcionará:

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

* Paso 2 - Evaluar el uso de eval:

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

* Paso 3 - sustituto volver

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

Bueno, no es que no se pueda resolver en general. Es un problema mal definido como es (¿qué constituye una expresión para evaluar y qué no?) Pero una vez que lo aclaramos, se resuelve fácilmente, si realmente te molestas en analizar cosas en lugar de abusar de 'eval'. – delnan

+1

@delnan: si esto es abuso de 'eval', entonces ¿qué no es abuso de 'eval '(en cuyo caso, no debería eliminarse del lenguaje por completo?). Creo que este es un buen lugar para usar 'eval' dado que el problema es lo suficientemente limitado como para analizar la expresión a evaluar desde la cadena de entrada. – mgilson

+0

Por mi parte, 'eval 'no debe colocarse de manera destacada (es decir, en el espacio de nombre global). Conozco casos de uso para 'compile' y' exec' (y son significativamente diferentes de esto, ya que controlan la cadena de entrada al 100% y se sabe lo que hará). Todavía tengo que encontrar un buen caso de uso para 'eval': cuando quiera evaluar una expresión matemática, escriba un evaluador de yarda de derivación o algo así. Si desea ejecutar el código de Python, use 'exec' porque es menos restringido. No evito 'eval' para la corrección, pero para no mezclar el código con datos. (Atornille a los chicos de lisp que dicen que el código es información.) – delnan

2

Aquí está mi intento:

>>> 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' 

Notas:

Esto es muy simple, no tratará con ecuaciones complejas, como aquellas con raíz cuadrada, pi, etc. pero creo que está en el espíritu de lo que está la pregunta. Para una respuesta robusta realmente ver el question posted by jeffery_the_wind; pero creo que puede ser excesivo para este caso simplista.

0

Para una solución sin usar eval, esto es lo que haría. Comience por encontrar todas las expresiones matemáticas en la cadena, que definiré como una cadena que contiene espacios, paréntesis, números y operaciones, y luego tira a los partidos que son todos los espacios en blanco:

>>> 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] 

A continuación, evaluar las expresiones que utilizan la clase NumericStringParser de this question, que utiliza pyparsing:

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

Entonces, para sustituir los resultados de nuevo en la expresión, revertir ordenar las expresiones de su índice de partida y colocarlos de nuevo en la cadena original:

>>> 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 veces es mejor simplificar la pregunta en lugar de encontrar soluciones complicadas.Es posible que desee para simplificar el problema haciendo que su código de proporcionarse como esto

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

esta manera se puede analizar usando una expresión regular simple y eval lo que hay dentro. De lo contrario, estás en una gran complejidad.

0

Mi opción:

>>> 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

Gracias a todos por su ayuda. En realidad, mi ejemplo proporcionado es muy simple comparado con lo que tengo en la tarea real. Leo estas cuerdas de archivo y, a veces puede tener es vista como esta:

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

ecuación matemática son simples, pero puede que ocurre muchas veces en una cadena, y debe ser evaluado por separado.

así que escribir un código de ejemplo, que es capaz de manejar estos casos: tal vez no es tan buena, pero resuelve el 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

[Sé que esto es una cuestión de edad, pero vale la pena señalar nuevas soluciones útiles como pop-up]

Desde python3.6, esta capacidad es ahora integrado en el lenguaje, acuñado "F-cuerdas".

Ver: PEP 498 -- Literal String Interpolation

Por ejemplo (nótese el f prefijo):

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' 
Cuestiones relacionadas