用Python實現復雜的計算器,可以按照“()”、乘除、加減的優先級進行混合運算。主旨是對正則表達式進行學習。
設計思路:
1.在計算式中搜索最后一個“(”,再搜索和它匹配的“)”,截取兩個括號間的表達式cul。
2.乘除的優先級高,循環搜索cul內的乘除號,進行計算后進行加減計算,得到結果ans。
3.用ans替換“(cul)”,重新搜索括號,知道將輸入的公式簡化成沒有括號的四則運算。
4.對最后的四則運算計算結果。流程圖如下:
設計時的注意事項:
1.在簡化計算式過程中,如3*(1-3),簡化后為3*-2,在進行乘運算的時候會報錯。解決措施為將乘數前的負號移至被乘數前。將算術式更換為-3*2。除法也用相同的方法處理。
2.在出現“--”或“+-”的時候,可以把“--”替換成“+”,把“+-”替換成“-”。
代碼分析:
代碼的結構是這樣的:
執行文件其實沒啥說的!
1 import os,sys 2 BASE_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 3 print(BASE_dir) 4 sys.path.append(BASE_dir) 5 from core import main 6 if __name__ == '__main__': 7 main.run()
calculato.py就是將文件路徑添加到環境變量中
1 import re 2 def mul(processor): #乘運算 3 a = re.search(r"(\d+)?[.]?(\d+)[*]", processor).group()[:-1] 4 b = re.search(r"[*]\d+[.]?(\d+)?", processor).group()[1:] 5 ans = float(a) * float(b) 6 ans = str(ans) 7 processor_past = re.sub(r"\d+[.]?(\d+)?[*]\d+[.]?(\d+)?", ans, processor, count=1) 8 return processor_past 9 def div(processor): #除運算 10 a = re.search(r"(\d+)?[.]?(\d+)[/]", processor).group()[:-1] 11 b = re.search(r"[/]\d+[.]?(\d+)?", processor).group()[1:] 12 ans = float(a) / float(b) 13 ans = str(ans) 14 processor_past = re.sub(r"\d+[.]?(\d+)?[/]\d+[.]?(\d+)?", ans, processor, count=1) 15 return processor_past 16 def add(processor): #加運算 17 a=re.search("(\d+)?[.]?(\d+)[+]",processor).group()[:-1] 18 b=re.search("[+]\d+[.]?(\d+)?",processor).group()[1:] 19 ans = float(a)+float(b) 20 ans = str(ans) 21 processor_past = re.sub(r"\d+[.]?(\d+)?[+]\d+[.]?(\d+)?",ans,processor,count=1) 22 return processor_past 23 def sub(processor): #減運算 24 a=re.search("\d+[.]?(\d+)?[-]",processor).group()[:-1] 25 b=re.search("[-]\d+[.]?(\d+)?",processor).group()[1:] 26 ans = float(a)-float(b) 27 ans = str(ans) 28 processor_past = re.sub(r"\d+[.]?(\d+)?[-]\d+[.]?(\d+)?",ans,processor,count=1) 29 return processor_past
在func.py中定義了運算的方法,由於形參是字符串形式,需將計算結果轉換成字符串,然后將結果和算術式替換:如mul("3*2+1)的返回值是“6+1”。
這里還用了字符串的切片方法,刪掉第一個值:[1:],刪掉最后一個值為[:-1]。
以乘法為例,"*"前必須為數字,被乘數是小數的話還會有小數點和整數位,所以在正則表達式的查詢關鍵字為r“(\d+)?[.]?(\d+)[*]”,所引出字符串后刪掉最后的“*”,而“*”后緊跟的是數字,小數點和小數點后的數為非必須字符。用r"[*]\d+[.]?(\d+)?"查找。
在查詢關鍵字時“*"和”+“為轉義字符,要在字符串前加”r“
下面是calcu.py的代碼
1 import re 2 from core import func 3 def find_brackets(cul): #將“()”內表達式分析出來 4 while re.search("[(].+", cul): 5 cul = re.search("[(].+", cul).group()[1:] 6 cul = cul[::-1] 7 while re.search("[)].+", cul): 8 cul = re.search("[)].+", cul).group()[1:] 9 cul = cul[::-1] 10 return (cul) 11 12 def change(search,ans,cul): #將運算結果和()里的表達式更換 13 search = re.sub("[+]", '\+',search) 14 search = re.sub("[*]", '\*',search) 15 cul = re.sub("[(]%s[)]"%search,ans,cul)#在正則表達式中插入變量 16 return cul 17 18 def change_minus(search, ans, before): # 和move_minus一起移動乘除號后面的負號 19 search = re.sub("[+]", '\+', search) 20 search = re.sub("[*]", '\*', search) 21 after = re.sub(search, ans, before) 22 return after 23 24 def move_minus(cul): 25 b = cul 26 if re.search(r"(\d+)?[.]?\d+[*][-](\d+)?[.]?\d+?", cul): 27 a = re.search(r"(\d+)?[.]?\d+[*][-](\d+)?[.]?\d+?", cul).group() 28 c = a 29 a = re.sub("[-]", "", a) 30 a = '-' + a 31 b = change_minus(c, a, cul) 32 elif re.search(r"(\d+)?[.]?\d+[/][-](\d+)?[.]?\d+?", cul): 33 a = re.search(r"(\d+)?[.]?\d+[/][-](\d+)?[.]?\d+?", cul).group() 34 c = a 35 a = re.sub("[-]", "", a) 36 a = '-' + a 37 b = change_minus(c, a, cul) 38 return b 39 40 41 def mul_div(str_past): #乘除優先級一致,進行乘除運算 42 ans = str_past 43 while re.search("[\*]|[/]",ans): 44 res = re.search("[/]|[\*]",ans).group() 45 if res == "*": 46 ans = func.mul(ans) 47 elif res =="/": 48 ans = func.div(ans) 49 return ans 50 51 def add_reduce(str_past): #加減優先級一致,進行加減運算 52 ans = str_past 53 ans = re.sub("--","+",ans) 54 ans = re.sub(r"[+][-]","-",ans) 55 while re.search(r"[+]|[-]",ans): 56 if re.match("-",ans): 57 break 58 else: 59 res = re.search(r"[+]|[-]",ans).group() 60 if res == "+": 61 ans = func.add(ans) 62 elif res =="-": 63 ans = func.sub(ans) 64 return ans 65 66 def cul(str): 67 cul = str 68 ans = str 69 while re.search("[(]",cul): 70 cul = re.sub("--", "+", cul) 71 cul = re.sub(r"[+][-]", "-", cul) 72 cul_1 = find_brackets(cul) 73 ans = mul_div(cul_1) 74 ans = add_reduce(ans) 75 ans = change(cul_1,ans,cul) 76 cul = ans 77 cul = move_minus(cul) 78 ans = move_minus(ans) 79 ans = mul_div(ans) 80 ans = add_reduce(ans) 81 return ans
calcu.py定義了整個計算的流程。
cul()中不斷索引括號,將索引出的算術式計算后用結論替代。每簡化一輪后檢查乘除號后是否有負號的狀態。
乘除法的優先級比加減高,可以把乘除的運算放在一起,
while re.search("[\*]|[/]",ans):
用這個方法可以從左到右循環搜索算術式內的乘除運算。
1 import re 2 import os,sys 3 from core import calcu,func.log 4 def run(): 5 print("歡迎使用計算器!!!") 6 while True: 7 cul = input("請輸入計算公式:") 8 if cul == "exit": 9 print("退出計算器!") 10 break 11 elif re.findall("[(]", cul).__len__() != re.findall("[)]", cul).__len__(): 12 print("括號不匹配!") 13 elif re.search("[(]|[)]",cul): 14 print("請使用英文符號!") 15 elif re.search("[a-z]|[A-Z]",cul): 16 print("請輸入數字!") 17 else: 18 ans = calcu.cul(cul) 19 print(ans) 20 log.logger(cul,ans)
main.py調用了整個算式的計算過程,並對輸入的公式進行了一些糾錯措施並在每次計算后將計算式和結論存儲在日志中。
1 import time 2 def logger(cul,ans): 3 fp = open("logger.txt",'a+',encoding="UTF-8") 4 now = time.time() 5 fp.write("%s %s=%s\n"%(now,cul,ans)) 6 fp.close()
log.py就是將每次運算的公式和結論存儲在日志中。
存在的問題:日志文件應存儲在log路徑下,但是現在每次執行完會在bin路徑下生成新日志文件並進行存儲操作。還在想辦法改進中!
感謝@callmeVcc的指正,上面的代碼有一個BUG:
按照上面的字符串輸入,輸出明顯和需求不同,分析原因是因為在calcu.py中,我最開始的思路是在括號索引以后把純加減乘除的計算過一遍,先過乘除,再過加減,但是在走加減的時候有個問題:
如果字符串是2-1這種的,被減數是正數的情況,沒什么問題,但是如果是負數,在上面第56行里,通過一個 if判斷,如果字符串和-metch的話就退出循環,當時考慮不周,錯誤的思路如下:
如果字符串里不含正負號,就是計算完畢,while循環跳出
如果包含一個負號,就是計算結果為負數,while跳出
但是-2+3這種情況也在if判斷中也是為True的,遇到這種情況也會直接跳出while循環
現在的思路就不用if判斷了,直接從字符串的第2個元素開始匹配,就防止出現表達式第第一個數值為負數值無法跳出,
在進行加減法運算的時候,取算數表達式左邊的數值時要考慮到值為負的情形。

1 """ 2 calcu.py 3 """ 4 # Author:Aaron 5 import re 6 from . import func 7 def find_brackets(cul): #將“()”內表達式分析出來 8 while re.search("[(].+", cul): 9 cul = re.search("[(].+", cul).group()[1:] 10 cul = cul[::-1] 11 while re.search("[)].+", cul): 12 cul = re.search("[)].+", cul).group()[1:] 13 cul = cul[::-1] 14 return (cul) 15 16 def change(search,ans,cul): #將運算結果和()里的表達式更換 17 search = re.sub("[+]", '\+',search) 18 search = re.sub("[*]", '\*',search) 19 cul = re.sub("[(]%s[)]"%search,ans,cul) #在正則表達式中插入變量 20 return cul 21 22 def change_minus(search, ans, before): # 和move_minus一起移動乘除號后面的負號 23 search = re.sub("[+]", '\+', search) 24 search = re.sub("[*]", '\*', search) 25 after = re.sub(search, ans, before) 26 return after 27 28 def move_minus(cul): 29 b = cul 30 if re.search(r"(\d+)?[.]?\d+[*][-](\d+)?[.]?\d+?", cul): 31 a = re.search(r"(\d+)?[.]?\d+[*][-](\d+)?[.]?\d+?", cul).group() 32 c = a 33 a = re.sub("[-]", "", a) 34 a = '-' + a 35 b = change_minus(c, a, cul) 36 elif re.search(r"(\d+)?[.]?\d+[/][-](\d+)?[.]?\d+?", cul): 37 a = re.search(r"(\d+)?[.]?\d+[/][-](\d+)?[.]?\d+?", cul).group() 38 c = a 39 a = re.sub("[-]", "", a) 40 a = '-' + a 41 b = change_minus(c, a, cul) 42 return b 43 44 45 def mul_div(str_past): #乘除優先級一致,進行乘除運算 46 ans = str_past 47 while re.search("[\*]|[/]",ans): 48 res = re.search("[/]|[\*]",ans).group() 49 if res == "*": 50 ans = func.mul(ans) 51 elif res =="/": 52 ans = func.div(ans) 53 return ans 54 55 def add_reduce(str_past): #加減優先級一致,進行加減運算 56 ans = str_past 57 ans = re.sub("--","+",ans) 58 ans = re.sub(r"[+][-]","-",ans) 59 #下面的匹配是從字符串的第2個元素開始,防止字符串是以負數開頭進入死循環 60 while re.search(r"[+]|[-]",ans[1:]): 61 res = re.search(r"[+]|[-]",ans[1:]).group() 62 #表達式里包含加減號,按照從左到右的順序依次計算 63 if res == "+": 64 ans = func.add(ans) 65 elif res =="-": 66 ans = func.sub(ans) 67 #沒有+-,返回值 68 return ans 69 70 def cul(str_input): 71 cul = str_input 72 ans = str_input 73 while re.search("[(]",cul): 74 cul = re.sub("--", "+", cul) 75 cul = re.sub(r"[+][-]", "-", cul) 76 cul_1 = find_brackets(cul) 77 ans = mul_div(cul_1) 78 ans = add_reduce(ans) 79 ans = change(cul_1,ans,cul) 80 cul = ans 81 cul = move_minus(cul) 82 ans = move_minus(ans) 83 ans = mul_div(ans) 84 ans = add_reduce(ans) 85 return ans

1 """ 2 func.py 3 """ 4 # Author:Aaron 5 import re 6 7 def mul(processor): 8 a = re.search(r"(\d+)?[.]?(\d+)[*]", processor).group()[:-1] 9 b = re.search(r"[*]\d+[.]?(\d+)?", processor).group()[1:] 10 ans = float(a) * float(b) 11 ans = str(ans) 12 processor_past = re.sub(r"\d+[.]?(\d+)?[*]\d+[.]?(\d+)?", ans, processor, count=1) 13 return processor_past 14 15 def div(processor): 16 a = re.search(r"(\d+)?[.]?(\d+)[/]", processor).group()[:-1] 17 b = re.search(r"[/]\d+[.]?(\d+)?", processor).group()[1:] 18 ans = float(a) / float(b) 19 ans = str(ans) 20 processor_past = re.sub(r"\d+[.]?(\d+)?[/]\d+[.]?(\d+)?", ans, processor, count=1) 21 return processor_past 22 23 def add(processor): 24 a=re.search("[-]?(\d+)[.]?(\d+)?[+]",processor).group()[:-1] 25 b=re.search("[+]\d+[.]?(\d+)?",processor).group()[1:] 26 27 ans = float(a)+float(b) 28 ans = str(ans) 29 30 processor_past = re.sub(r"{}\+{}".format(a,b),ans,processor,count=1) 31 32 return processor_past 33 34 def sub(processor): 35 #a-b,獲取a和b的值 36 a=re.search("[-]?(\d+)[.]?(\d+)?[-]",processor).group()[:-1] 37 b=re.search("[-]\d+[.]?(\d+)?",processor[1:]).group()[1:] #獲取b的值時候取巧,用對表達式進行切片防止拿到-1-2狀態下的第一個負號后的值 38 # print(a) 39 # print(b) 40 ans = float(a)-float(b) 41 ans = str(ans) 42 43 processor_past = re.sub(r"{}\-{}".format(a,b),ans,processor,count=1) 44 return processor_past
修改后的程序運行結論