计算器的思路主要是先用正则匹配出括号内的计算,然后用计算的结果将括号及内容整个替换掉,再匹配括号进行计算,替换,直至将括号内的计算全部完成
再者就是需要用正则匹配出乘除法先进行计算,再用正则匹配加减法进行计算,同时需要注意空格及符号的处理,如:++,—,/-,*-,或是负数开头的公式的计算
r"d+(.d+)?/-d+(.d+)?" #匹配3/-2形式的整数或小数的乘除计算 r"d+(.d+)?*-d+(.d+)?" #匹配3*-2形式的整数或小数的乘除计算 r"d+(.d+)?[/*]d+(.d+)?" #匹配正常的整数或小数的乘除计算 r"-d+(.d+)?-d+(.d+)?" #匹配-3-2形式的整数或小数的加减计算 r"-d+(.d+)?+d+(.d+)?" #匹配-3+2形式的整数或小数的加减计算
具体如下:
import re # func = "1 - 2 * ((60 - 30 + (-40 / 5) * (9 - 2 * 5 / 3 + 7 / 3 * 99 / 4 * 2998 + 10 * 568 / 14)) # - (-4 * 3) / (16 - 3 * 2))" func = " 1+2*-(-3+2)/5.6+3" func = func.replace(" ", "") print(func) print(1+2*-(-3+2)/5.6+3) # 筛选内层括号及括号内容函数 def fun_inside(a): com = re.compile(r"([^()]+)") bracket = com.finditer(a) for j in bracket: yield j.group() # 去空格,处理符号函数 def formatting(a): a = a.replace(" ", "") a = a.replace("++", "+") a = a.replace("--", "+") a = a.replace("+-", "-") a = a.replace("-+", "-") a = a.replace("*+", "*") a = a.replace("/+", "/") return a # 括号内的乘除法 def cal_pursue_divide(a): s = re.findall("[*/]", a) s_list = re.split("[*/]", a) res = None for index, j in enumerate(s_list): if res: if s[index - 1] == "*": res *= float(j) elif s[index - 1] == "/": res /= float(j) else: res = float(j) return res # 括号内的加减法 def cal_add_minus(a): s = re.findall("[+-]", a) s_list = re.split("[+-]", a) res = None for index, j in enumerate(s_list): if res: if s[index - 1] == "+": res += float(j) elif s[index - 1] == "-": res -= float(j) else: res = float(j) return res # 四则运算包含特殊的"-","*-","/-","-"开头等 def cal_in_four(a): while True: if re.search(r"[/*]", a): # 先计算乘除法 a = formatting(a) if re.search(r"d+(.d+)?/-d+(.d+)?", a) or re.search(r"d+(.d+)?*-d+(.d+)?", a): # 整数或小数 # 计算 3/-2 类型 if re.search(r"d+(.d+)?/-d+(.d+)?", a): # 整数或小数 result_high = re.search(r"d+(.d+)?/-d+(.d+)?", a) ret = result_high.group().replace("/-", "/") res = cal_pursue_divide(ret) a = a.replace(result_high.group(), "-" + str(res)) # 计算 3*-2 类型 elif re.search(r"d+(.d+)?*-d+(.d+)?", a): # 整数或小数 result_high = re.search(r"d+(.d+)?*-d+(.d+)?", a) ret = result_high.group().replace("*-", "*") res = cal_pursue_divide(ret) a = a.replace(result_high.group(), "-" + str(res)) # 正常的乘除计算 else: if re.search(r"d+(.d+)?[/*]d+(.d+)?", a): # 整数或小数 result_high = re.search(r"d+(.d+)?[/*]d+(.d+)?", a) res = cal_pursue_divide(result_high.group()) a = a.replace(result_high.group(), str(res)) # 计算加减法 elif re.search(r"[+-]", a[2:]): a = formatting(a) # 计算"-"开头的加减法 if a[1:].startswith("-") or a.startswith("-"): # 计算 -3-2 类型 if re.search(r"-d+(.d+)?-d+(.d+)?", a): # 整数或小数 result_low = re.search(r"-d+(.d+)?-d+(.d+)?", a) ret = result_low.group()[1:] res = cal_add_minus(ret.replace("-", "+")) a = a.replace(result_low.group(), "-" + str(res)) # 计算 -3+2 类型 elif re.search(r"-d+(.d+)?+d+(.d+)?", a): # 整数或小数 result_low = re.search(r"-d+(.d+)?+d+(.d+)?", a) ret = result_low.group()[1:] res = cal_add_minus(ret.replace("+", "-")) a = a.replace(result_low.group(), "-" + str(res)) # 计算正常的加减法 else: if re.search(r"d+(.d+)?[+-]d+(.d+)?", a): # 整数或小数 result_low = re.search(r"d+(.d+)?[+-]d+(.d+)?", a) res = cal_add_minus(result_low.group()) a = a.replace(result_low.group(), str(res)) else: if re.search(r"d+(.d+)?[+-]d+(.d+)?", a): # 整数或小数 result_low = re.search(r"d+(.d+)?[+-]d+(.d+)?", a) res = cal_add_minus(result_low.group()) a = a.replace(result_low.group(), str(res)) return a else: return a # 计算器函数 def calculator(s): while True: if re.search(r"(.+)", s): ret_receive_view = fun_inside(s) for i in ret_receive_view: ret = cal_in_four(i).replace("(", "").replace(")", "") s = s.replace(i, ret) else: result = cal_in_four(s) result = formatting(result) s = formatting(result) if s.startswith("+"): return "结果是:" + s[1:] else: return "结果是:" + s sr = calculator(func) print(sr)
改进版本如下,上个版本存在的问题主要有,加减乘除的运算过程中对负号的处理十分复杂,且逻辑混乱,在某些复杂的负数类运算中会出现运算逻辑的问题,导致计算结果出现偏差
下面的版本主要优化负数的四则运算,思路是不考虑负号,以[+ - * /]进行分割,将分割的结果(可能是整数,或是负数)进行计算,从而避免了负号运算出现的问题
import re # 去空格,处理符号函数 def formatting(a): a = a.replace("++", "+") a = a.replace("--", "+") a = a.replace("+-", "-") a = a.replace("-+", "-") return a # 加减法计算 def cal_add_sub(a): # 正则匹配出 全部的正负整数和小数 # 注意此处需要用?:去掉小数的优先级 否则将出现 ['', '.3', '.3'] ret = re.findall('[+-]?d+(?:.d+)?', a) sum_as = 0 # 此处的计算四路是找出全部的正负整数和小数不考虑数字的正负直接将所有数字累加 # 从而解决负数相减等问题 for i in ret: sum_as += float(i) return sum_as # 正常乘除法计算 def cal_pursue_divide(a): # 四则运算中的乘除法需从左至右计算,所以每次的计算都是两个数之间进行的 # 所以以 *|/进行分割,进行两个数之间的乘除运算,避免了负号的干扰 if '*' in a: i, j = a.split('*') return str(float(i) * float(j)) elif '/' in a: i, j = a.split('/') return str(float(i) / float(j)) # 乘除负数的计算 def mul_div(a): while True: # 正则匹配出 正负整数和小数之间的乘除法 ret = re.search('d+(.d+)?[*/]-?d+(.d+)?', a) if ret: atom_a = ret.group() # 1*-22 # 将匹配的结果用正常的乘除法则进行运算 res = cal_pursue_divide(atom_a) a = a.replace(atom_a, res) else: return a # 四则运算 def cal_in_four(a): # 先进性乘除法的运算 a = mul_div(a) # 乘除运算完成后会出现- -或+ -等情况需要进行符号的格式化 a = formatting(a) # 格式化完成进行加减运算 a_sum = cal_add_sub(a) return a_sum # 筛选内层括号及括号内容函数 def fun_inside(a): com = re.compile(r"([^()]+)") bracket = com.finditer(a) for j in bracket: # 用生成器方便下面对括号的逐个运算 yield j.group() # 计算器的主函数 def calculator(s): # 对输入的算式去空格 s = s.replace(" ", "") while True: # 匹配到括号就进行循环去括号 if re.search(r"(.+)", s): ret_receive_view = fun_inside(s) # i是取出的括号及括号内的内容 for i in ret_receive_view: # ret是括号内的运算结果,并且将括号去掉 ret = cal_in_four(i.replace("(", "").replace(")", "")) # 此处应注意上面无论是加减还是乘除的运算,函数的返回值全部是float类型 # 所以与str进行替换时需要进行转换类型 s = s.replace(i, str(ret)) s = formatting(s) # 匹配不到括号进行最后的四则运算 else: result = cal_in_four(s) # 上面已经提到最终的运算结果为float类型 return result li = "1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )" result_final = calculator(li) print(result_final)