Python使用正则匹配re实现eval计算器
一、 概述
本文仅为记录自己学习过程中踩过的坑,不喜勿喷。
二、方案思路
string = '2 * 60 - 30 + (-40.0/5) * (9-2*5/2+9/3*10/4*2+10*5/5 -(-4*3)) -(-4*3)/(16-3*2)'
1.计算未出现括号情况下的结果
2.把最里层括号找出来,计算后替换回原字符串
3.重复计算到没有括号为止,再次用未出现括号方法计算,得出最后结果
三、源码
1 #!-*- coding:utf-8 -*- 2 #__anthor__:"Klay Zhu" 3 #date: 2018/7/13 4 import re 5 PREC = '{:+.5f}'#计算精度 5代表5位小数 6 obj_mul = re.compile(r"(-?d+.?d*)*(-?d+.?d*)") # 乘法 7 obj_div = re.compile(r"(-?d+.?d*)/(-?d+.?d*)") # 除法 8 obj_add = re.compile(r"(-?d+.?d*)+(-?d+.?d*)") # 加法 9 obj_sub = re.compile(r"(-?d+.?d*)-(-?d+.?d*)") # 减法 10 obj_f = re.compile(r"(?+?-?d+)?") # 检查括号内是否运算完毕规则 11 obj_k = re.compile(r"([^()]+)") # 寻找最内层括号规则 12 obj_che = re.compile(r"[^0-9+-*/()s.]") # 匹配输入非法字符 13 def count(string): 14 """ 15 计算没有括号的式子 16 :param string: 17 :return: 18 """ 19 string = rm_SP(string) 20 while True: 21 if obj_div.search(string) :# 除法(必须先判断) 22 exp = re.split(r'/',obj_div.search(string).group()) 23 answer = float(exp[0]) / float(exp[1]) 24 string = obj_div.sub(PREC.format(answer),string,1) 25 elif obj_mul.search(string) :# 乘法 26 exp = re.split(r'*', obj_mul.search(string).group()) 27 answer = float(exp[0]) * float(exp[1]) 28 string = obj_mul.sub(PREC.format(answer), string,1) 29 elif obj_add.search(string) :# 加法 30 exp = re.split(r'+', obj_add.search(string).group()) 31 answer = float(exp[0]) + float(exp[1]) 32 string = obj_add.sub(PREC.format(answer), string,1) 33 elif obj_sub.search(string) :# 减法 34 exp = re.split(r'-', obj_sub.search(string).group()) 35 if exp[0] =="":#前面有负号的时候 36 answer = 0-float(exp[1]) - float(exp[2]) 37 string = obj_sub.sub(PREC.format(answer), string, 1) 38 else: 39 answer = float(exp[0]) - float(exp[1]) 40 string = obj_sub.sub(PREC.format(answer), string,1) 41 elif obj_f.search(string):# 结束循环 42 string = re.sub('(|)|', '', string) 43 return string 44 string = rm_SP(string) #去除多余的符号 45 46 def rm_SP(string): 47 """ 48 去除符号 49 :param str: 50 :return: 51 """ 52 string = re.sub('++','+',string) 53 string = re.sub('--', '+', string) 54 string = re.sub('+-', '-', string) 55 string = re.sub('-+', '-', string) 56 string = re.sub('/+', '/', string) 57 string = re.sub('*+', '*', string) 58 string = re.sub('^+', '', string) 59 return string 60 61 def rm_PAR(string): 62 """ 63 计算括号里 64 :param string: 65 :return: 66 """ 67 string = rm_SP(string) 68 list_m = obj_k.findall(string) 69 list_map= map(count,list_m) 70 for i in range(len(list_m)): 71 string = string.replace(list_m[i],list_map.__next__(),1) 72 return string 73 def main(string): 74 """ 75 主函数,循环计算 76 :param string: 77 :return: 78 """ 79 checkout(string) 80 string = re.sub('s', '', string) #去掉空格 81 while True: 82 if obj_k.search(string):#是否还存在括号 83 string = rm_PAR(string) 84 continue 85 answer = count(string) 86 return answer 87 88 def checkout(string): 89 """ 90 校验输入是否合法 91 :param string: 92 :return: 93 """ 94 if len(re.findall('(',string)) != len(re.findall(')',string)): 95 print("括号不成对") 96 exit() 97 elif obj_che.findall(string) != [] : 98 print(obj_che.findall(string)) 99 print("输入中有非法字符") 100 exit() 101 102 if __name__ == '__main__': 103 string = r'2 * 60 - 30 + (-40.0/5) * (9-2*5/2+9/3*10/4*2+10*5/5 -(-4*3)) -(-4*3)/(16-3*2)'#-236.80000 104 # string = input("请输入需要计算的式子:") 105 answer = main(string) 106 print("最后答案:", answer)
四、记录踩下的坑
1.使用map()计算出结果后替换回原字符串时,由于上一次替换后满足下次正则匹配,所以被深深坑了一波!后来改用完全匹配就没出现这个问题了。
2.在处理负数转换成字符串的时候,可以用format() ,具体如何使用请百度。