文字列内の数式の評価について質問があります。たとえば、私の文字列は次のとおりです。
my_str='I have 6 * (2 + 3) apples'
この文字列を評価して次の結果を得る方法を知りたいです。
'I have 30 apples'
これを行う方法はありますか?
前もって感謝します。
この場合、 PSpythonのeval
関数は役に立ちません。関数で評価しようとすると、エラーが発生しましたeval
。
文字列内の数式の評価について質問があります。たとえば、私の文字列は次のとおりです。
my_str='I have 6 * (2 + 3) apples'
この文字列を評価して次の結果を得る方法を知りたいです。
'I have 30 apples'
これを行う方法はありますか?
前もって感謝します。
この場合、 PSpythonのeval
関数は役に立ちません。関数で評価しようとすると、エラーが発生しましたeval
。
これが私の試みです:
>>> 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'
ノート:
これは非常に単純です。複雑な方程式 (平方根や円周率など) は扱いませんが、質問の目的の精神に基づいていると思います。本当に堅牢な回答については、 jeffery_the_wind によって投稿された質問を参照してください。しかし、この単純なケースではやり過ぎかもしれません。
複雑な解決策を考え出すよりも、質問を単純化したほうがよい場合もあります。コードを次のように提供することで、問題を単純化することができます
my_str='I have {6 * (2 + 3)} apples'
このようにして、単純な正規表現を使用して解析し、中身を評価できます。そうしないと、非常に複雑になります。
ご協力いただきありがとうございます。実際、私が提供した例は、実際のタスクに比べて非常に単純です。これらの文字列をファイルから読み取り、次のようなビューを表示できる場合があります。
my_str='ENC M6_finger_VNCAPa (AA SYZE BY (0.14*2)) < (0.12 + 0.07) OPPOSITE REGION'
数式は単純ですが、1 つの文字列で何度も発生する可能性があるため、個別に評価する必要があります。
そこで、このケースを処理できるサンプル コードを作成します。あまり良くないかもしれませんが、問題を解決します。
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 -- 入力をサニタイズします。これは、一般的に行うのが最も難しい部分です。基本的に、文字列を壊さずに単一の数式を文字列から引き出す方法が必要です。ここでは、単純な正規表現が機能します。
sanitized = re.sub(r'[a-zA-Z]','',my_str).strip()
*ステップ 2 -- 以下を使用して評価しeval
ます。
value = eval(sanitized, {'__builtins__':None})
*step 3 -- 背中の付け替え
new_string = my_str.replace(sanitized, str(value))
私のオプション:
>>> 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'
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]
次に、pyparsingを使用するこの質問NumericStringParser
のクラスを使用して式を評価します。
>>> 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'