评估字符串中的数学表达式

-3 投票
1 回答
876 浏览
提问于 2025-04-18 06:21

我的问题很简单,假设我有一个字符串,比如 s = 'line $sin(2*x) $x*cos(x) $x',而我已经知道了 x 的值(比如是1.0),现在我想把这个字符串计算出来,变成类似 s = line 0.909 -0.416 1.0' 的样子。你可以看到字符串 s 中有三个数学表达式:每个表达式都是以 $ 符号开头,并且以空格或行尾结束。

这个想法应该很简单:使用正则表达式和 re.sub 函数。我之前对正则表达式一无所知,经过一两个小时的学习,我掌握了一些基础知识,但还是搞不清楚怎么写出一个合适的模式来匹配这三个表达式,每个表达式单独匹配。如果我能成功做到这一点,剩下的部分就简单了,使用 eval() 来计算表达式,转换成字符串,然后把整个字符串组合起来返回。下面是我写的代码。

import re
from math import *

# parameters
x = 1.0
# test strings
s = 'line $sin(2*x) $x*cos(x) $x'

p = '\$[\s+]'

def replacer(s):
    if s.startswith('$'):
        return eval(s[1:])
    else:
        return "ERROR"

print re.sub(p,replacer,s)

我确定我的正则表达式是错的,因为它没有捕捉到这三个模式,我尝试了很多不同的模式……有没有人能帮我找到一个有效的模式?然后我想我就可以继续做其他部分了。更新:问题解决了,见下面的选定答案。

1 个回答

2

你第一个遇到的问题是,你在正则表达式中使用了 [\s+],这个只会匹配空格或者加号一次。
你想要的正确表达式是 \$(\S+),它会把除了$符号以外的所有内容放到一个捕获组里,方便后面使用。

其次,你需要用更符合Python风格的方式来编写代码。把随机的变量和表达式到处乱放是不可重用的。
相反,应该把重复的变量放在一个函数的范围内,这样长期来看会减少麻烦。

在这个情况下,你需要的正则函数不是 re.sub,而是 re.findall。这个函数会遍历字符串中的所有匹配项。

你会注意到我在使用正则之前先编译了它,这样可以让代码看起来更整洁。

最后,我们会遍历一个非常简单的 list() 数据类型,里面是我们的正则匹配结果。

如你所见,你可以简单地在任何表达式和任何值上调用 evaluate_expression() 函数。

import re
from math import *


def evaluate_expression(equation, **kwargs):
    for key in kwargs:
        exec key + " = " + str(kwargs[key])  # Creates x variable
    parser = re.compile(r'\$(\S+)')
    expressions = parser.findall(equation)
    for expression in expressions:
        print eval(expression)

evaluate_expression('line $sin(2*x) $x*cos(x) $x', x=1.0)

撰写回答