首页 > 技术文章 > Python使用正则匹配re实现eval计算器

Klay 2018-07-14 15:43 原文

 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)
View Code

四、记录踩下的坑

1.使用map()计算出结果后替换回原字符串时,由于上一次替换后满足下次正则匹配,所以被深深坑了一波!后来改用完全匹配就没出现这个问题了。

2.在处理负数转换成字符串的时候,可以用format() ,具体如何使用请百度。

 

推荐阅读