需要实现的计算式
source='1 - 2 * ((60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14)) - (-4*3)/ (16-3*2))'
思路一
第一步:把表达式进行规范化,如表达式中的空格,进行检测
source='1 - 2 * ((60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14)) - (-4*3)/ (16-3*2))'
def check(s): ##检测表达式里不规范的的输入
flag=True
if re.findall('[a-zA-Z]',s): #判断是否有字母
print('Invalid')
flag=False
return flag ##先打个标记,返回一个值,false证明检测是有问题
def format(s): ##把空格之类多余的输入进行转换
s=s.replace(' ','') #不需要匹配,对里面所有的空格替换就可以,再用s接收
s=s.replace('++','+')
return s ##处理后,返回一个s
if check(source):
strs=format(source) #检查后,源数据由source变成strs
第二步:设计思路
分析:表达式分为有括号和无括号两种情况
a. 有括号的情况
检测表达式中是否有括号,\(,因为上面检测时,就判断左右括号是一致的,这时只需要使用一边的括号就可以,while的循环是会继续走else
while re.search('\('):
pass ##先处理有括号的,从最里面的括号开始
else:
pass ##再处理没有括号的
实际
while re.search('\('):
strs=re.search('\([^()]+\)',strs).group() #已经匹配到,但是匹配到可能有加减乘除,不会有括号
取最里层括号的过程:这里要取的是(2+4)
In [5]: import re
In [6]: res=re.search('\([^()]\)','((2+4)*2)') ##[^()]是不为括号的内容,2+4是三个元素,所以在[^()]后面加下+号
In [7]: print(res)
None
In [8]: res=re.search('\([^()]+\)','((2+4)*2)')
In [9]: print(res)
<_sre.SRE_Match object; span=(1, 6), match='(2+4)'>
In [10]: print(res.group())
(2+4) ####\([^()]+\)+,\(\)是匹配(),[^()]是匹配2+4中的其中一个内容,[^()]+是2+4
In [11]: res=re.search('\([^()]+\)','(1+1+(2+4)*2)') ##无论(2+4)外加多少内容都可以匹配
In [12]: print(res.group())
(2+4)
b.对括号中运算进行判断,乘除的优先级比较高
假设小括号内的内容为(2+5*3),就要先运算5*3
def cal_mul_div(s): #进行乘除法的运算,所以这里要可以检测到5*3
return s #返回s,(2+5*3)=(2+15)
def cal_add_sub(s): #(2+15) #加减的函数
return s #(17)
while re.search('\('):
strs=re.search('\([^()]+\)',strs)
strs=cal_mul_div(strs) #(2+15)
strs=cal_add_sub(strs) #(17) 实际17是字符串可以做切片处理,str[1:-1]=17,-1是最后一个不取
else: #else 处理的是没有括号的,所以直接放入乘除,再到加减就可以
strs=cal_mul_div(strs)
strs=cal_add_sub(strs)
c.乘除的处理
分析:(1).乘的过程中可能有整数相乘如5*3,还有小数相乘的情况如5.4*3,整数的情况可以是\d*\d最简单的,但是它不确定位数,\d+*\d+可以使用+号重复前面数字的次数,这样位数就可以随机变化
(2).小数的相乘,\d+.\d+*\d+,这种写法存在问题,对于出现整数相乘就不行,所以小数点是可有可无,使用?来实现0次或者1次,应该为d+.?\d+*\d+,但还有问题如就匹配5*3就不能匹配,首先\d+必须要有一个数字,5是匹配上了,点是可以没有,但是后面的\d+,如果使用+号表示一定要有,整数5后面是没有,所以要使用*号(0到无穷)为\d*
(3).乘法为得到两个数字都要一样:\d+\.?\d*\*\d+\.?\d*
(4).乘除同时使用[*/] : \d+\.?\d* [*/] \d+\.?\d*
(5).对得到的字符值使用split进行切分
(6).使用If检测是乘法还是除法,决定下面的操作
(7).直接使用float对字符串进行一个强转换
(8).替换原来的结果replace
正则匹配乘除
def cal_mul_div(s): #(2+5*3)
re.search('\d+\.?\d*[*/]\d+\.?\d*',s)
return s
使用split切分
def cal_mul_div(s): #(2+5*3)
re.search('\d+\.?\d*[*/]\d+\.?\d*',s)
#(2+15),得到一个字符串后,要做切分
x,y=re.split('[*/]',ret) #使用*乘号或/除号进行切分,得到字符'5','3'作为元素在一个序列,再使用两个值去接收
return s
切分后对乘法或除法使用if判断
def cal_mul_div(s): #(2+5*3)
re.search('\d+\.?\d*[*/]\d+\.?\d*',s)
#(2+15),得到一个字符串后,要做切分
x,y=re.split('[*/]',ret) #使用*乘号或/除号进行切分,得到字符'5','3'作为元素在一个序列,再使用两个值去接收
if .....
return s
对浮点型进行操作
def cal_mul_div(s): #(2+0.5*3.8)
ret1=re.search('\d+\.?\d*[*/]\d+\.?\d*',s)
x,y=re.split('[*/]',ret) #使用*乘号或/除号进行切分,得到字符'0.5','3.8'作为元素在一个序列,再使用两个值去接收
ret2=float(x)*float(y) #得到一个浮点行
return s
要把ret得到结果替换原来的结果#(2+0.5*3.8)
def cal_mul_div(s): #(2+0.5*3.8)
ret1=re.search('\d+\.?\d*[*/]\d+\.?\d*',s)
x,y=re.split('[*/]',ret) #使用*乘号或/除号进行切分,得到字符'0.5','3.8'作为元素在一个序列,再使用两个值去接收
ret2=float(x)*float(y) #得到一个浮点行
str(ret2) #'1.9'
s.replace(ret1,ret2) #把ret1替换成ret2
return s #(2+1.9)
思路二:具体实现
源数据
source='1 - 2 * ((60-30 + (-9-2-5-4*5-6/3-40/8+5*9)) - (-4*3)/ (16-3*2))'
#source = "2 - ((-30/3)*(3-2))"
#source ="4**8"
#source = "4*7*2" 最简单的,没括号的
1. 对表达式进行检查,判断字符的规范
if check_expression(source):
print("source: ", source)
print("eval result: ", eval(source))
source = format_string(source)
print(source)
(1).check_expression函数,检查表达式的合法性
def check_expression(string):
check_result = True
#括号是否匹配
if not string.count("(") == string(")"): #判断左括号和右括号的数量是否相等
print("表达错误,括号示闭合!")
check_result = False
if re.findall('[a-z]+', string.lower()): #判断是否有字母,有就显示非法字符
print("表达式错误,包含非法字符!")
check_result = False #修改标志位
return check_result #最后返回标记位时,默认是为True,一旦有错误就为false
(2). 表达式没问题,需要对它进行格式化
format_string函数,格式化字符串
def format_string(string):
string = string.replace("--", "+") #把这些不规范的进行一个替换
string = string.replace("-+", "-")
string = string.replace("++", "+")
string = string.replace("+-", "+")
string = string.replace("*-", "*")
string = string.replace("*+", "*")
string = string.replace("/+", "/")
string = string.replace(' ', '')
return string
2.计算
(7 /3*99/4*2998 +10 * 568/14)把最里层的括号取出后,如果要做乘法,可能不只一个乘法,有可能是连着相乘,需要做循环处理,如while时,应该判断检测是不是有符合条件的表达式,是一个数乘以一个数,或者一个数除以一个数,如果检测出来就应该一直做乘除法的计算,直接计算只剩加减法,
while source.count("(") > 0: #在最外层,首先判断是否有括号,在下面进行处理
strs = re.search('\([^()]*\)', source).group() #取最里层括号的内容,然后进行加减乘除运算,一般先做乘除,再做加减
replace_str = calc_mul_div(strs) #将括号的表达式进行乘除运算,将要处理的字符串strs传入到calc_mul_div乘除的函数
replace_str = calc_add_sub(replace_str) #将括号的表达式进行加减运算
source = format_string(source.replace(strs, replace_str[1:-1])) #将括号的字符串替换为计算结果,结果包含(),替换时去掉():[1:-1]
else: #没有括号的表达式在else处理
replace_str = calc_mul_div(source)
replace_str = calc_add_sub(replace_str)
source = source.replace(source, replace_str)
print("my result: ", source.replace("+",""))
乘除的函数
def calc_mul_div(string): #已经把字符串取得,如(49+4*9),要对4*9做运算,如果是(49+4*9-8/2)要对4*9和8/2做运算
regular='\d+\.?\d*([*/]|\*\*)[\-]?\d+\.?\d*' #定规则,从字符串中获取一个乘法和除法的表达式,[\-]?匹配一个负号
while re.findall(regluar, string): #使用findall检测是否有符合规则的字符串,在while循环中,无论有多少乘号或者除号可以计算
expression = re.search(regular, string).group() #相当于得到4*9,放入列表,用search只能取一个,接下来就是要使36替换4*9
if expression.count("*")==1: #如果是乘法
x,y = expression.split("*") #直接使用*为分隔
mul_result = str(float(x) * float(y)) #进行计算
string = string.replace(expression, mul_result) #计算完要替换原来的string(49+4*9-8/2),把36替换4*9,新的string=(49+36-8/2)
string = format_string(string) #把string格式化下
#(49+36-8/2)会再进入while re.findall(regular, string),因为里面还有个8/2
if expression.count("/"): #如果是除法,与乘法的一样
x,y = expression.split("/")
div_result = str(float(x) / float(y))
string = string.replace(expression, div_result) #得到值string=(49+36+4)
string = format_string(string)
if expression.count('*')==2: #幂等
x,y=expression.split('**')
pow_result=1
for i in range(int(y)):
pow_result*=int(x)
string=string.replace(expression,str(pow_result))
string=format_string(string)
return string #经过一系列的循环检测,经过乘除法后得到string=(49+36+4),这里暂时不忽略幂运算
3.经过乘除计算后,进入加减法
while source.count("(") > 0:
strs = re.search('\([^()]*\)', source).group() #去括号,得到括号的字符串(-9-2-5-4*5-6/3-40/8+5*9)
replace_str = calc_mul_div(strs)
#将括号的表达式进行乘法和除法运算得到(-9-2-5-20-2-5+45),新的replace_str去替换下面加减的replace_str
replace_str = calc_add_sub(replace_str) #进行加减运算
source = format_string(source.replace(strs, replace_str[1:-1]))
加减运算调用的函数calc_add_sub
def calc_add_sub(string): #加减法函数中传入的string,如(4+5-6+20-9)
add_sub= regular='[\-]?\d+\.?\d*([+-][\-]?\d+\.?\d*' #加减法正则匹配,加减中有个规则:依中序遍历由左而右计算
while re.findall(add_sub, string):
add_sub_ex = re.search(add_sub, string).group()
if add_sub_ex.count("+")==1:
x,y = add_sub_ex.split("+")
add_result = str(float(x) + float(y))
string = string.replace(add_sub_ex, add_result)
string = format_string(string)
if add_sub_ex.count("-"): ##减法时可能出现 -4-5的情况,可能使'-'切分,然后放入列表['',4,5],再把后两个数相加,再替换成-9
sub_list = re.search(add_sub, string)
for sub_str in sub_list:
numbers = sub_str.split("-")
if len(numbers) == 3:
result = 0
for v in numbers;
if v:
result -= float(v)
else:
x,y = numbers
result = float(x) - float(y)
string = string.replace(sub_str, "+" + str(result))
string = format_string(string)
return string
完整代码


1 [root@python3 day1]# cat calculator.py 2 #!/usr/local/python3/bin/python3 3 import re 4 5 def format_string(string): 6 string = string.replace("--", "+") 7 string = string.replace("-+", "-") 8 string = string.replace("++", "+") 9 string = string.replace("+-", "+") 10 string = string.replace("*-", "*") 11 string = string.replace("*+", "*") 12 string = string.replace("/+", "/") 13 string = string.replace(' ', '') 14 return string 15 16 def calc_mul_div(string): 17 regular='\d+\.?\d*([*/]|\*\*)[\-]?\d+\.?\d*' 18 while re.findall(regluar, string): 19 expression = re.search(regular, string).group() 20 21 if expression.count("*")==1: 22 x,y = expression.split("*") 23 mul_result = str(float(x) * float(y)) 24 string = string.replace(expression, mul_result) 25 string = format_string(string) 26 27 if expression.count("/"): 28 x,y = expression.split("/") 29 div_result = str(float(x) / float(y)) 30 string = string.replace(expression, div_result) 31 string = format_string(string) 32 33 if expression.count('*')==2: 34 x,y=expression.split('**') 35 pow_result=1 36 for i in range(int(y)): 37 pow_result*=int(x) 38 string=string.replace(expression,str(pow_result)) 39 string=format_string(string) 40 return string 41 42 def calc_add_sub(string): 43 add_sub= regular='[\-]?\d+\.?\d*([+-][\-]?\d+\.?\d*' 44 45 while re.findall(add_sub, string): 46 add_sub_ex = re.search(add_sub, string).group() 47 48 if add_sub_ex.count("+")==1: 49 x,y = add_sub_ex.split("+") 50 add_result = str(float(x) + float(y)) 51 string = string.replace(add_sub_ex, add_result) 52 string = format_string(string) 53 54 if add_sub_ex.count("-"): 55 sub_list = re.search(add_sub, string) 56 for sub_str in sub_list: 57 numbers = sub_str.split("-") 58 if len(numbers) == 3: 59 result = 0 60 for v in numbers; 61 if v: 62 result -= float(v) 63 else: 64 x,y = numbers 65 result = float(x) - float(y) 66 string = string.replace(sub_str, "+" + str(result)) 67 string = format_string(string) 68 return string 69 70 def check_expression(string): 71 check_result = True 72 if not string.count("(") == string(")"): 73 print("表达错误,括号示闭合!") 74 check_result = False 75 if re.findall('[a-z]+', string.lower()): 76 print("表达式错误,包含非法字符!") 77 check_result = False 78 return check_result 79 80 source='1 - 2 * ((60-30 +(-40/5) * (-9-2-5-4*5-6/3-40/8+5*9) - (-4*3)/ (16-3*2))' 81 82 if check_expression(source): 83 print("source: ", source) 84 print("eval result: ", eval(source)) 85 source = format_string(source) 86 print(source) 87 88 while source.count("(") > 0: 89 strs = re.search('\([^()]*\)', source).group() 90 replace_str = calc_mul_div(strs) 91 replace_str = calc_add_sub(replace_str) 92 source = format_string(source.replace(strs, replace_str[1:-1])) 93 94 else: 95 replace_str = calc_mul_div(source) 96 replace_str = calc_add_sub(replace_str) 97 source = source.replace(source, replace_str) 98 print("my result: ", source.replace("+",""))
4.做完加减法的运算后,只是处理了最里层的一个括号,还要继续使用while循环处理