本次作业对re模块的要求还不算太高.
题目:
实现能计算类似 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式的计算器程序
要求: 用户输入表达式,通过分析字符串来计算,不能使用eval
先普及一下寻找一个任意数字的正则表达式: [-+]?\d+(\.?\d+)?匹配任意带符号的数字.但在python中使用时,为了避免()带来的组优先问题,需要添加?:来破功[-+]?\d+(?:\.?\d+)?
分解:
1. 首先分析,表达式不支持"带未知数"的运算, 所以判断表达式格式:
表达式字符串全部由数字和+-*/()以及小数点组成,不满足条件则返回重新输入
2. 数学运算,此处不考虑如1**2或4//2这种计算方式
re.findall('[*/][*/]', exp) # 如果返回值不为空则判断为错误输入,需重新输入
3. 需要先将表达式去空格: 直接exp.replace(' ', '')
4. 因作业要求中,表达式带括号,所以需要先找到最简括号算数表达式,计算出结果后替换到原表达式中,重新匹配最简算数表达式
re.findall('\([^()]+\)', exp) # \(以(开头,为避免与表达式组混淆,需用\转义;[^()]+匹配至少一个字符,该字符中不存在()符号; \)以)结尾,同\(
需要注意的是,替换回去的时候一定是无括号的.所以,如果计算结果是负数,会有这种情况: ...2-(-40/5)...结果...2--8...,因此需要对符号精简,也是用到数学方法
exp = exp.replace('++', '+').replace('+-', '-').replace('--', '+').replace('-+', '-') # 精简符号,同时也可避免输入时手抖多输入符号...
5. 计算时,优先乘除运算.因乘除运算可单独将数字提取出来后,计算出的结果再添加符号,如-3*2可先计算3*2=6,再将符号还给他(当然这是下一步的加减运算,也可以不考虑)
同上一条,也有可能如: ...2*(-40/5)...结果...2*-8...,所以表达式在寻找带有*和/的表达式时,需要将后面数字的符号考虑进去
min_exp_list = re.findall('\d+(?:\.?\d+)?[/*][-+]?\d+(?:\.?\d+)?', exp) # 找带*/的表达式:数字[*/]数字 !!!*/号右边可能为负数(带符号)
6. 待括号表达式和乘除表达式计算完成并替换后,需要精简一下符号,也就需要再次调用replace('++', '+')
7. 开始计算剩下的所有加减运算.
此时,可以光明正大的偷个懒,因为没有括号没有乘除符号,只剩下加减运算了,而所有的加减运算,都可以看成是两个带+-号的数字相加,故可直接带符号匹配每一个数字
re.findall('[-+]?\d+(?:\.?\d+)?', exp) # 同样,为避免()带来的优先匹配,需要手动添加?:破功
结果是一个带符号的数字的字符串组成的列表,将表中元素逐个转换为float类型,再sum(list)求和即可得出最后的结果
So Easy...
可是调了好久...
Block的地方:
1. 正则表达式的写法...(好吧,这个熟练就好了. 快速校验传送门: http://tool.chinaz.com/regex/
2. 匹配括号计算完毕,替换结果后括号还在...
3. 到底是用re.search逐个匹配+计算+替换还是用re.findall一次性匹配,再遍历list逐个计算+替换呢?
4. 匹配乘除运算时,最容易忽视被乘/除数是否带符号,一旦是带符号的,结果就返回None了.这里卡了好久
...
本次作业,正则表达式貌似只与数字打交道了.字母什么的\w啊\W啊什么的,有机会再看吧
...
To Be Continued...