Python - calculate math expression inside a string

I have a question about evaluating a mathematical expression inside a string. For example, my line is as follows:

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

I am wondering how to evaluate this line and get the following result:

 'I have 30 apples' 

Can this be done?

Thanks in advance.

PS In this case, the python eval function does not help. When trying to evaluate using the eval function, she raised an error.

+8
source share
8 answers

Here is my attempt:

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

Notes:

It is very simple, it will not deal with complex equations - for example, with a square root, pi, etc., but I believe that this is in the spirit of what is being said. For a truly reliable answer, see the question posted by jeffery_the_wind ; but I believe that this may be redundant for this simplified case.

+2
source

Sometimes itโ€™s better to simplify the question rather than come up with complex solutions. You might want to simplify this problem by providing code in this way

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

That way you can parse it with a simple regular expression and determine what's inside. Otherwise, you have a lot of difficulties.

+2
source

This is a very complex problem, which is probably almost impossible to solve as a whole. However, here is an easy way to attack a problem that works with sample input.

* Step 1 - sanitize the entrance. This is the hardest thing to do in general. Basically, you need a way to get one mathematical expression out of a line without distorting it. A simple regular expression will work here:

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

* Step 2 - rate using eval :

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

* Step 3 - substitute back

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

Thank you all for your help. In fact, my above example is very simple compared to what I have in a real task. I read this line from a file, and sometimes it can look like this:

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

The mathematical equation is simple, but can occur many times in one line and must be evaluated separately.

So, I am writing an example of code that is able to handle these cases: Maybe this is not so good, but to solve the problem:

 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 
+1
source

For a solution without using eval, here is what I will do. Start by looking for all the mathematical expressions in the string, which I will define as a string containing spaces, brackets, numbers and operations, and then cross out the matches, all spaces:

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

Then evaluate the expressions using the NumericStringParser class from this question , which uses pyparsing :

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

Then, to substitute the results back into the expression, reverse the sorting of the expressions by their starting index and put them back in the original line:

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

My option:

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

Use f-strings or threaded.

F-string: f'I have {str(6*(2+3))} apples'

0
source

[I know this is an old question, but it is worth noting new useful solutions as they appear]

Starting with python3.6, this feature is now built into the language , came up with "f-lines" .

See: PEP 498 - Interpolation of Letter Strings

For example (note the prefix f ):

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

All Articles