Python使用正则表达式实现字符串算式计算
本文介绍如何使用Python正则表达式替代内置的eval()函数来计算包含括号的字符串算式。通过匹配最内层括号,依次执行乘除和加减运算,并逐步替换结果,最终实现安全、原生的算式解析逻辑。
Python正则表达式计算字符串算式实现
在Python中,存在一种便捷且实用的方法可以直接计算字符串形式的数学表达式。
这种方法便是使用内置的eval()函数。
s = '1+2*(6/2-9+3*(3*9-9))'
print(eval(s))
#97.0
然而,当前的目标是尝试使用正则表达式来实现类似的功能。
首先,我们需要获取一个待计算的算式,例如字符串'1+2*(6/2-9+3*(3*9-9))'。
根据基础算术规则,计算应当从最内层的括号开始进行。
那么,如何提取最内层括号及其内部的算式呢?我们可以借助正则表达式来完成这一任务。
import re
pattern = re.compile(r'\([^(^)]*?\)')
s = '1+2*(6/2-9+3*(3*9-9))+(6-3)'
ret = pattern.findall(s)
print(ret)
#['(3*9-9)','(6-3)']
此时,我们已经成功提取出最内层括号及其包含的算式,第一步即告完成。
接下来,我们需要将提取到的内容去除括号。
由于最内层括号可能不止一个,因此我们需要使用一个新的列表来存储所有去除括号后的算式。
ret2 = []
pattern2 = re.compile(r'\((?P<tag>.*?)\)')
for i in range(len(ret)):
ret3 = pattern2.search(ret[i])
ret2.append(ret3.group('tag'))
print(ret2)
#['3*9-9', '6-3']
至此,我们实际上已经完成了大部分工作。
第三步,我们需要对提取出的最内层括号算式进行具体计算。
此处仅展示核心逻辑,以'3*9-9'(即ret2[1])为例进行说明。
计算过程仍需遵循算术优先级,即先执行乘除,再执行加减。
首先是乘除运算的处理。
乘除运算逻辑
def mul_div(s):
if '*' in s:
s = s.split('*')
return float(s[0]) * float(s[1])
elif '/' in s:
s = s.split('/')
return float(s[0]) / float(s[1])
while True:
pattern3 = re.compile(r'[-+*/]?(?P<tag>-?\d+(\.\d+)?[*/]-?\d+(\.\d+)?)')
ret3 = pattern3.search(ret2[1])
try:
ret4 = ret3.group('tag')
except Exception as e:
pass
if '*' not in ret2[1] and '/' not in ret2[1]:
break
else:
ret2[1] = ret2[1].replace(ret4, str(mul_div(ret4)))
关于这段代码,若存在疑惑,此处进行详细解释。
首先,mul_div()是我们自定义的一个用于计算乘除法的方法。
受限于正则表达式的约束以及使用search方法,匹配到的字符串仅可能呈现为'字符/字符'或'字符*字符'的形式。因此,对字符串进行切割后,必然得到一个包含两个元素的列表。
计算出'字符/字符'或'字符*字符'的值后,我们需要用该计算结果替换正则匹配到的原始字符串,直到该字符串中不再包含乘除号为止。
接下来处理加减运算。
加减运算逻辑
def add_sub(s):
if '+' in s:
s = s.split('+')
return float(s[0]) + float(s[1])
else:
if s[0] == '-':
s = s[1::].split('-', 1)
s[0] = '-' + s[0]
return float(s[0]) - float(s[1])
else:
s = s.split('-', 1)
return float(s[0]) - float(s[1])
while True:
pattern3 = re.compile(r'-?\d+(\.\d+)?[-+]-?\d+(\.\d+)?')
ret3 = pattern3.search(ret2[i])
try:
ret4 = ret3.group()
except Exception as e:
pass
if '+' not in ret2[i] and '-' not in ret2[i][1::]:
break
else:
ret2[i] = ret2[i].replace(ret4, str(add_sub(ret4)))
加减法的处理逻辑与上述乘除法相似,区别不大。
唯一的区别在于退出判断时,数值可能为负数,因此不能直接判断负号是否存在,而需要判断除第一个位置外,其余位置是否还存在负号。
第四步。
在此阶段,所有最内层括号计算得出的数值均存储在ret2列表中,而ret1中存放的是包含括号及其内部算式的原始字符串。因此,我们需要将两者进行替换。
def str_replace(lst1,lst2):
for i in range(len(lst1)):
global str1
str1 = str1.replace(lst1[i], lst2[i])
str_replace(ret1,ret2)
第五步。
至此,距离成功仅剩最后一步。
可能你已经注意到,这一整套流程仅对括号内的算式进行运算。若原始字符串没有括号,则不会对其执行操作。因此,我们可以在字符串输入时,在最外层包裹一层括号即可解决此问题。
str1 = '1+2*(6/2-9+3*(3*9-9))'
str1 = '( )'.replace(' ',str1)
随后,获取一个算式并重复上述步骤,直到字符串中不再包含括号为止。
完整代码实现如下。
完整代码实现
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
# 乘除法
def mul_div(s):
if '*' in s:
s = s.split('*')
return float(s[0]) * float(s[1])
elif '/' in s:
s = s.split('/')
return float(s[0]) / float(s[1])
# 加减法
def add_sub(s):
if '+' in s:
s = s.split('+')
return float(s[0]) + float(s[1])
else:
if s[0] == '-':
s = s[1::].split('-', 1)
s[0] = '-' + s[0]
return float(s[0]) - float(s[1])
else:
s = s.split('-', 1)
return float(s[0]) - float(s[1])
# 替换字符串
def str_replace(lst1,lst2):
for i in range(len(lst1)):
global str1
str1 = str1.replace(lst1[i], lst2[i])
# 匹配最内层括号
pattern1 = re.compile(r'\([^(^)]*?\)')
str1 = '1+2*(6/2-9+3*(3*9-9))'
str1 = '( )'.replace(' ',str1)
while True:
if '(' not in str1 and ')' not in str1:
break
ret1 = pattern1.findall(str1)
# 匹配括号内的内容
ret2 = []
pattern2 = re.compile(r'\((?P<tag>.*?)\)')
for i in range(len(ret1)):
ret = pattern2.search(ret1[i])
ret2.append(ret.group('tag'))
# 计算乘除法
while True:
pattern3 = re.compile(r'[-+*/]?(?P<tag>-?\d+(\.\d+)?[*/]-?\d+(\.\d+)?)')
ret3 = pattern3.search(ret2[i])
try:
ret4 = ret3.group('tag')
except Exception as e:
pass
if '*' not in ret2[i] and '/' not in ret2[i]:
break
else:
ret2[i] = ret2[i].replace(ret4, str(mul_div(ret4)))
# 计算加法
while True:
pattern3 = re.compile(r'-?\d+(\.\d+)?[-+]-?\d+(\.\d+)?')
ret3 = pattern3.search(ret2[i])
try:
ret4 = ret3.group()
except Exception as e:
pass
if '+' not in ret2[i] and '-' not in ret2[i][1::]:
break
else:
ret2[i] = ret2[i].replace(ret4, str(add_sub(ret4)))
str_replace(ret1,ret2)
print(str1)
#97.0
结束语。
希望未来读者看到此代码时,不要吐槽ret1至ret4的变量命名方式。
此外,不确定是否已表述清晰,读者能否理解,毕竟熬夜未眠,思维可能不够敏捷。
此代码肯定存在诸多可优化之处,仅供参考。
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
总结。
以上所述是小编给大家介绍的Python使用正则实现计算字符串算式,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持! 如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









