Python攻克之路-计算器思路

需要实现的计算式

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("+",""))
View Code

 

4.做完加减法的运算后,只是处理了最里层的一个括号,还要继续使用while循环处理

 

posted @ 2018-04-28 13:49  Reid21  阅读(776)  评论(0编辑  收藏  举报