[ Python ] 计算器
1. 开发要求
编写一个计算器,禁止使用 eval() 等直接返回计算结果的函数,通过计算逻辑和正则完成,计算表达式中包含各种运算符及小括号。
2. 需求分析
实现思路:
(1)首先获取小括号中的内容,递归计算小括号中的表达式,没有括号直接计算表达式
(2)使用正则表达式匹配字符串,用计算结果替换计算表达式
程序流程图:
程序所需知识点:
(1)字符串的处理
(2)正则表达式的编写
(3)递归函数的理解
3. 程序代码
# 作者:hkey # 博客地址:https://www.cnblogs.com/hukey/p/9324744.html # 功能实现:使用正则表达式和递归实现计算器功能。 # 使用说明: 直接输入表达式,返回最终计算结果 q 退出
# -*- coding: utf-8 -*- # Author: hkey import re def check_exp(get_input): ''' 检查表达式中的字符是否存在不合法性,合法返回真,不合法返回假 :param get_input: 用户输入的表达式,待检测的表达式 :return: 返回真或者假 ''' char_set = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/', '(', ')', '**', '%', '//', '.', } # 有效表达式的字符集合 # 判断用户输入的表达式字符是否是char_set的子集,如果是则为真,否则为假 if set(get_input) <= char_set: # 如果表达式以 '*, /, %, )'开头,则为假 if get_input.startswith('*') or get_input.startswith('/') or get_input.startswith('%') \ or get_input.startswith(')'): print('\33[31;1m输入的表达式有误,请重新输入\33[0m') return False # 如果表达式以 '+, -, *, /, ('结尾,则为假 elif get_input.endswith('+') or get_input.endswith('-') or get_input.endswith('*') \ or get_input.endswith('/') or get_input.endswith('('): print('\33[31;1m输入的表达式有误,请重新输入\33[0m') return False # 如果表达式中含有 '.., +/, +/, -*, -/, -%',则为假 elif '..' in get_input or '+*' in get_input or '+/' in get_input or '+%' in get_input \ or '-*' in get_input or '-/' in get_input or '-%' in get_input or '%%' in get_input: print('\33[31;1m输入的表达式有误,请重新输入\33[0m') return False else: return True else: print('\33[31;1m表达式中包含其他字符,无法进行计算,请重新输入\33[0m') return False def simple_exp(exp): ''' 简化表达式,如'+-' 简化为 '-',返回最简表达式 :param exp: 用户输入的表达式 :return: 简化后的表达式 ''' # 初始化一个替换字符列表 replace_char_list = [('+-', '-'), ('-+', '-'), ('++', '+'), ('--', '+'), ('*+', '*'), ('/+', '/'), ('%+', '%'), ('//+', '//'), ('**+', '**')] flag = False # 初始化标识符 count = 0 # 初始化不匹配次数 while not flag: # 使用 flag循环的目的是为了避免出现 '1+++3'这样情况 for i in replace_char_list: if i[0] in exp: # 要简化的字符在表达式中 exp = exp.replace(i[0], i[1]) # 替换需要简化的字符 break # 中断for循环,进行下一次while循环,避免出现 '1+++3'这样情况 else: count += 1 # 如果replace_char_list 等于 count,说明没有需要替换的字符,退出循环 if len(replace_char_list) == count: flag = True # 如果表达式以'+'开头,就去掉'+' if exp.startswith('+'): exp = exp[1:] return exp def power(exp): ''' 表达式的幂运算,并将计算结果替换为匹配到的表达式 :param exp: 去除括号后的表达式 :return: 返回计算后的表达式 ''' match = re.search('[\+\-]*\d*\.?\d*[\*]{2}[\+\-]*\d*\.?\d*', exp) # 匹配幂运算 # 如果匹配到就进行幂运算,否则直接返回表达式 if match: match_content = match.group() if match_content.split('**') > 1: n1, n2 = match_content.split('**') result = float(n1) ** float(n2) # 幂运算 exp = exp.replace(match_content, str(result)) # 用计算后的值替换匹配的表达式 exp = simple_exp(exp) # 检查替换后的表达式是否存在'+-'等字符,有的话替换为最简 return power(exp) # 递归执行匹配计算 else: return exp def mul_div(exp): ''' 表达式的乘除、取整、取余计算,并将计算结果替换为匹配到的表达式 :param exp: 去除括号后的表达式 :return: 返回计算后的表达式 ''' match = re.search('\d+\.?\d*[\*\/\/\/\%]+[\-]?\d+\.?\d*', exp) # 匹配 '乘除、取余、取整'表达式 if match: match_content = match.group() if len(match_content.split('*')) > 1: n1, n2 = match_content.split('*') result = float(n1) * float(n2) # 乘法计算 exp = exp.replace(match_content, str(result)) # 用计算后的值替换匹配的表达式 exp = simple_exp(exp) # 检查替换后的表达式是否存在'+-'等字符,有的话替换为最简 return mul_div(exp) # 递归执行匹配计算 elif len(match_content.split('/')) > 1: n1, n2 = match_content.split('/') result = float(n1) / float(n2) # 除法计算 exp = exp.replace(match_content, str(result)) # 用计算后的值替换匹配的表达式 exp = simple_exp(exp) # 检查替换后的表达式是否存在'+-'等字符,有的话替换为最简 return mul_div(exp) # 递归执行匹配计算 elif len(match_content.split('//')) > 1: n1, n2 = match_content.split('//') result = float(n1) // float(n2) # 取整计算 exp = exp.replace(match_content, str(result)) # 用计算后的值替换匹配的表达式 exp = simple_exp(exp) # 检查替换后的表达式是否存在'+-'等字符,有的话替换为最简 return mul_div(exp) # 递归执行匹配计算 elif len(match_content.split('%')) > 1: n1, n2 = match_content.split('%') result = float(n1) % float(n2) # 取余计算 exp = exp.replace(match_content, str(result)) # 用计算后的值替换匹配的表达式 exp = simple_exp(exp) # 检查替换后的表达式是否存在'+-'等字符,有的话替换为最简 return mul_div(exp) # 递归执行匹配计算 else: return exp def add_sub(exp): ''' 表达式的加减计算,并将计算结果替换为匹配到的表达式 :param exp: 去除括号后的表达式 :return: 返回计算后的表达式 ''' match = re.search('\-?\d+\.?\d*(\+|\-)\d+\.?\d*', exp) # 匹配加减表达式 if match: match_content = match.group() if len(match_content.split('+')) > 1: n1, n2 = match_content.split('+') result = float(n1) + float(n2) # 计算加法 exp = exp.replace(match_content, str(result)) # 将计算的值替换括号表达式 exp = simple_exp(exp) # 检查替换括号后的表达式是否存在 '++'等字符,存在的话先替换掉 return add_sub(exp) # 递归计算括号中的内容并替换 # 如果表达式以'-' 分隔,分为两种情况 1. '-1-1'.split('-') 等于三个元素, 2. '1-1'.split('-') 等于两个元素 elif len(match_content.split('-')) > 1: if match_content.split('-') == 3: n1, n2 = match_content.split('-')[1:] result = float('-' + n1) - float(n2) # 这里要注意 float('-' + n1) exp = exp.replace(match_content, str(result)) # 将计算的值替换括号表达式 exp = simple_exp(exp) # 检查替换括号后的表达式是否存在 '++'等字符,存在的话先替换掉 return add_sub(exp) # 递归计算括号中的内容并替换 elif len(match_content.split('-')) == 2: n1, n2 = match_content.split('-') result = float(n1) - float(n2) # 计算减法 exp = exp.replace(match_content, str(result)) # 将计算的值替换括号表达式 exp = simple_exp(exp) # 检查替换括号后的表达式是否存在 '++'等字符,存在的话先替换掉 return add_sub(exp) # 递归计算括号中的内容并替换 else: return exp def calculate(exp): ''' 获取一个表达式,返回计算结果 :param exp: 表达式 :return: 运算结果 ''' result = power(exp) # 执行幂运算,返回计算结果 result = mul_div(result) # 执行乘除、取整、取余运算,返回结算结果 result = add_sub(result) # 执行加减运算,返回计算结果 return result def parenthesis(exp): ''' 匹配括号内的表达式并计算结果,将结果替换括号中的内容,最终返回计算结果 :param exp: 字符检查替换后最简的表达式 :return: 表达式最终计算结果 ''' match = re.search('\([^()]+\)', exp) # 匹配括号内的表达式 # 如果取到括号内的表达式,则去除括号,计算表达式结果并替换括号的内容 if match: content = match.group()[1:-1] # 通过索引剔除表达式的括号 result = calculate(content) # 计算不带括号表达式的结果 print('计算前的表达式:\33[32;1m%s\33[0m' % exp) print('括号中运算结果:\33[32;1m%s=%s\33[0m' % (content, result)) result_content = '(' + content + ')' exp = exp.replace(result_content, str(result)) # 将计算的值替换括号表达式 exp = simple_exp(exp) # 检查替换括号后的表达式是否存在 '++'等字符,存在的话先替换掉 print('计算后的表达式:\33[32;1m%s\33[0m\n' % exp) parenthesis(exp) # 递归计算括号中的内容并替换 # 如果表达式没有匹配到括号,则直接计算表达式结果 else: result = calculate(exp) # 计算表达式结果 print('表达式运算结果:\33[32;1m%s\33[0m' % result) if __name__ == '__main__': print('\33[33;1m欢迎使用计算器\33[0m'.center(30, '#')) while True: get_input = input('请输入表达式 | 退出(q) :').strip() if not get_input: continue if get_input == 'q': break get_input = ''.join(re.split('\s+', get_input)) if check_exp(get_input): exp = simple_exp(get_input) print('简化后的表达式:\33[32;1m%s\33[0m' % exp) parenthesis(exp)
4. 程序执行过程
计算 3*( -4+ 50 )-(( 100 + 40 )*5/2- 3*2* 2/4+9)*((( 3 + 4)-4)-4) 的结果值