我有一個關於字符串內數學表達式求值的問題。 例如我的字符串如下:Python - 評估字符串內的數學表達式
my_str='I have 6 * (2 + 3) apples'
我想知道如何評價這個字符串,並得到以下結果:
'I have 30 apples'
是在沒有辦法做到這一點?
在此先感謝。
P.S. python的eval
函數在這種情況下不起作用。當嘗試使用eval
函數進行評估時,它提出了一個錯誤。
我有一個關於字符串內數學表達式求值的問題。 例如我的字符串如下:Python - 評估字符串內的數學表達式
my_str='I have 6 * (2 + 3) apples'
我想知道如何評價這個字符串,並得到以下結果:
'I have 30 apples'
是在沒有辦法做到這一點?
在此先感謝。
P.S. python的eval
函數在這種情況下不起作用。當嘗試使用eval
函數進行評估時,它提出了一個錯誤。
這是一個非常棘手的問題,可能幾乎不可能解決一般情況。但是,下面是一個簡單的方法來解決與示例輸入一起使用的問題。
*第1步 - 消毒輸入。一般來說,這是最難的部分。基本上,您需要一種方法將單個數學表達式從字符串中提取出來,而不會對其進行修改。這裏一個簡單的正則表達式將工作:
sanitized = re.sub(r'[a-zA-Z]','',my_str).strip()
*第2步 - 評估使用eval
:
value = eval(sanitized, {'__builtins__':None})
*第3步 - 回替補
new_string = my_str.replace(sanitized, str(value))
好吧,這並不是說它不能一般解決。這是一個不明確的問題(什麼構成要評估的表達式,什麼不是?),但是一旦我們指出了這個問題,如果你真的很麻煩解析一些東西而不是濫用'eval',就很容易解決。 – delnan
@delnan - 如果這是'eval'濫用,那麼什麼是'eval'濫用(在哪一點上,不應該完全從語言中刪除?)。我認爲這是一個使用'eval'的完全不錯的地方,因爲這個問題已經足夠用來解析要從輸入字符串中計算出來的表達式。 – mgilson
我認爲'eval'不應該放在顯着位置(即在全局名稱空間中)。我知道用於'compile'和'exec'的用例(它們與此有很大不同,因爲它們控制着輸入字符串100%,並知道它會做什麼)。我還沒有遇到'eval'的好用例 - 當你想評估一個數學表達式時,寫一個調車場評估器或其他東西。如果你想運行Python代碼,使用'exec',因爲它的限制較少。我並不是爲了正確而避免使用'eval',而是不要將代碼與數據混淆。 (擰說說代碼是數據的lisp傢伙。) – delnan
這裏是我的嘗試:
>>> 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'
備註:
這很簡單,它不會處理複雜的方程 - 像平方根,pi等那些方程,但我相信它符合問題之後的精神。對於確實健壯的答案見question posted by jeffery_the_wind;但我認爲對於這個過於簡單的案例來說可能是過分的。
對於沒有使用eval的解決方案,這是我會做的。通過查找所有的字符串,我會定義爲包含空格,括號,數字和操作的字符串中的數學表達式的開始,那麼剔除這都是空白的比賽:
>>> 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]
接下來,評估使用NumericStringParser
類的表達從this question,它使用pyparsing:
>>> nsp = NumericStringParser()
>>> results = [nsp.eval(my_str[e.start():e.end()]) for e in exprs]
>>> results
[30.0]
然後,將結果代回的表達,可以通過起始索引反轉排序的表達,並把它們放回原始字符串:
>>> 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'
有時最好簡化問題而不是提出複雜的解決方案。您可能希望通過爲您的代碼,以簡化提供這樣
my_str='I have {6 * (2 + 3)} apples'
這樣你可以使用一個簡單的regex和eval裏面有什麼分析它的問題。否則,你會遇到很多複雜問題。
我的選項:
>>> 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'
感謝所有您的幫助。實際上,我提供的例子非常簡單,它比較了我在實際任務中的情況。我從文件中讀取這些字符串,有時是可以有鑑於這樣的:
my_str='ENC M6_finger_VNCAPa (AA SYZE BY (0.14*2)) < (0.12 + 0.07) OPPOSITE REGION'
數學公式很簡單,但可以發生在一個字符串很多時間,應分別進行評估。
所以我寫了一個示例代碼,這是能夠處理這種情況: 也許不是這樣好,但解決問題:
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
[我知道這是一個老問題,但因爲他們彈出]
由於python3.6,現在這個能力內置到語言,創造「F-字符串」它值得指出的是新的有用的解決方案。
參見:PEP 498 -- Literal String Interpolation
例如(注意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'
這是功課? –
http://stackoverflow.com/questions/2371436/evaluating-a-mathematical-expression-in-a-string –
@jeffery_the_wind不是真正的重點,因爲(不像那樣)需要丟棄字符串的非數學部分。 –