字符组 : [字符组]
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用“ [ ] ”表示
字符分为很多类,比如数字、字母、标点等等。
假如要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一
\w 包括:字母、数字、下划线;如果验证要求只允许输入:“数字和字母” 不能单独 \w 解决,
还要考虑到下划线,所以只能写成[0-9a-zA-Z]
import re
strd = input("please:")
if re.search("^[0-9a-zA-Z]+$", strd):
print("输入正确")
else:
print("错误")
# [/s/S] 、[/d/D]、[/w/W] 这类的可以匹配任意字符
有一些有特殊意义的元字符进入字符组中会回复它本来的意义如: . | [ ] ( )
^ 匹配字符串的开始 如: ^x ,表示字符串必须以x开头, 注意:与 [^ ] 意义不同
$ 匹配字符串的结束 如: x$ , 表示字符串必须以x结尾
^,$ : 如果字符有多行, 且在 re.M 模式下, ^,$ 会将每一行当作一个字符串,
即换行后会重新匹配
\A 匹配字符串的开始, 与^ 类似, 如: \Ax ,表示字符串必须以x开头
\Z 匹配字符串的结尾, 与$类似, 如: x\Z,表示字符串必须以x结尾
# \A ,\Z , 在re.M模式下不会影响多行匹配
\b 用来匹配单词的结尾, 如: er\b 表示单词是否以 er结束
\B 用来匹配非单词的结尾, 如: er\B 表示单词中含有er,单独的以er结尾的词不行
# 注意:\b 需要转意 即写成 \\b
import re
res=re.findall("海.","海燕海桥海石") # ['海燕', '海桥', '海石']
res=re.findall("^海.","海燕海桥海石") # 海燕
res=re.findall("海.$","海燕海桥海石") # 海石
res=re.findall("^海.$","海燕海桥海石") # []
res=re.findall("^海.$","海燕") # ['海燕'] 这就能明白为什么 . 也属于边界字符了
print(res)
身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部由数字组成,首位不能为0;
如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:
# 匹配任意一个邮箱 [email protected]
# x|y 表示匹配 x 或者 y
# (xyz) 加上括号表示将 xyz 看成一个整体
mailPattern = "\w+@\w+\.\w+"
# mailPattern = "(\w+@\w+\.((com)|(cn)))" # 在re.match / re.search 下能找出
# 匹配日期 1800-01-01 --- 2020-12-31
# 1899 1999 -- 2020
# 0-[1-9] 1[0-1-2]
# 0-[1-9] 1 2 - 0-9 3 0,1
result=re.search("^((1[89]\d{2})|(20[01]\d)|(2020))-((0[1-9])|(1[012]))-((0[1-9])|([12]\d)|(3[01]))$","2020-12-31")
# 注意括号分组,或 | 的时候,每一种可能给一个括号,
# 判断的每一项(年、月、日)再给一个括号
print(result)
import re
strd = input("请按格式输入出生年月日:")
if re.search("((1[89]\d{2})|(20[01]\d)|(2020))-((0[1-9])|(1[012]))-((0[1-9])|([12]\d)|(3[01]))", strd):
print("输入正确")
else:
print("输入格式有误,或者数值有误")
贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
import re
res = re.findall("a?", "min is a good man") # 非贪婪模式
# 结果:['', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', 'a', '', '']
res = re.findall("a?", "min is a good maan") # 非贪婪模式
# 结果:['', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', 'a', 'a', '', '']
res = re.findall("a*", "min is a good maan") # 贪婪模式
# 结果:['', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', 'aa', '', '']
res = re.findall("a+", "min is a good maan") # 贪婪模式
# 结果:['a','aa']
res = re.findall("a{3}", "min is a good maaan") # 贪婪模式
# 结果:['aaa']
res = re.findall("a{3}", "min is a good maaaan") # 贪婪模式
# 结果:['aaa']
res = re.findall("a{3,5}", "min is a good maaaaan") # 贪婪模式
# 结果:['aaaaa'] 包后
res = re.findall("a{3,}", "min is a good maaaaaaan") # 贪婪模式
# 结果:['aaaaaaa']
res = re.findall("a{,5}", "min is a good maaaaaaan") # 贪婪模式
# 结果:['', '', '', '', '', '', '', 'a', '', '', '', '', '', '', '', 'aaaaa', 'aa', '', '']
print(res)
单独出现 ? 非贪婪模式
出现 的第二个范围量词是“ ?” 就会变成 非贪婪模式
常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
. * ? 的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
合在一起就是:取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x
就是取前面任意长度的字符,直到一个 x 出现
三种模式:
re.match() 是从源字符串的 起始位置开始查找一个匹配项
re.search() 是从源字符串中找到一个匹配项
re.findall() 是从源字符串中找到所有的匹配项
flag:真正的含义
re.I 使匹配对大小写不敏感
re.M 多行匹配,是否影响 ^ 和 $
re.S 使 . 匹配包含换行符在内的任意字符
# re.M 实例
import re
res = re.findall("^min","min is a good man\nmin is a good man") # ['min']
res = re.findall("^min", "min is a good man\nmin is a good man", re.M) # ['min', 'min']
# 如果后面,没有加上re.M 表示看做一行字符串(不会进行换行),只能找到前面的一个“min”,加了,加了能找到两个
res = re.findall("\Amin","min is a good man\nmin is a good man") # ['min']
res = re.findall("\Amin","min is a good man\nmin is a good man",re.M) # ['min', 'min']
# print(res)
re.match(pattern,string,flag) # 是从源字符串的 起始位置开始查找一个匹配项
pattern 要进行匹配的正则表达式
string 表示的是源字符串
flag 标记, 可以不写
re.I 使匹配对大小写不敏感
re.M 多行匹配,是否影响 ^ 和 $
re.S 使 . 匹配包含换行符在内的任意字符
# 如果匹配成功会返回一个对象
# 如果匹配失败 会 返回None
# 可以根据 结构是否为 None 来判断是否匹配成功
# 可以通过这个变量的group方法来获取结果;
# 如果没有匹配到,会返回None,此时如果使用group会报错
res = re.match("www","www.baidu.com")
res = re.match("www","aww.baidu.com")
res = re.match("www","awww.baidu.com")
res = re.match("www","wwbaidu.www,com")
res = re.match("www","wwwwbaidu.www,com")
res = re.match("www","WWW.baidu.com")
res = re.match("www","WWW.baidu.com",re.I)
print(res)
print(type(res))
>>> 匹配手机号码
import re
phone_number = input('please input your phone number : ')
if re.match('^(13|14|15|18)[0-9]{9}$',phone_number): # 如果成功则不是None
print('是合法的手机号码')
else:
print('不是合法的手机号码')
re.search(pattern,string,flag) # 是从源字符串中(从左往右)找到第一个匹配项
pattern 要进行匹配的正则表达式
string 表示的是源字符串
flag 标记, 可以不写
re.I 使匹配对大小写不敏感
re.M 多行匹配,是否影响 ^ 和 $
re.S 使 . 匹配包含换行符在内的任意字符
# 多用于表单验证
res = re.search("www","www.baidu.com")
res = re.search("www","ww.baiduwww.com")
res = re.search("www","www.baiduwww.com")
print(res)
print(res.group)
# 只匹配从左到右的第一个,得到的不是直接的结果,而是一个变量,
# --需要通过这个变量的group方法来获取结果;
# 如果没有匹配到,会返回None,此时如果使用group会报错
re.findall(pattern,string,flag) # 是从源字符串中找到所有的匹配项
pattern 要进行匹配的正则表达式
string 表示的是源字符串
flag 标记, 可以不写
re.I 使匹配对大小写不敏感
re.M 多行匹配,是否影响 ^ 和 $
re.S 使 . 匹配包含换行符在内的任意字符
# findall的结果是列表的形式,会将找到的多个结果放到列表中去
# 注意: 如果找不到,会返回一个空列表
# 注意:不能直接使用 group 方法
res = re.findall("www","www.baidu.com")
res = re.findall("wwwa","www.baiduwww.com") # 结果:[]
print(res)
print(type(res))
findall 的优先级查询:
import re
ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(ret) # ['oldboy'] 这是因为findall会优先把匹配结果组里内容返回
# 如果想要匹配结果,取消权限即可,格式:在分组内加上 ?:即 (?:正则表达式)
ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(ret) # ['www.oldboy.com']
>>> 三种模式练习
import re
ret = re.findall('a', 'eva egon yuan') # 返回所有满足匹配条件的结果,放在列表里
print(ret) # 结果 : ['a', 'a']
ret = re.search('a', 'eva egon yuan').group()
print(ret) # 结果 : 'a'
# 函数会在字符串内 查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,
# 该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
ret = re.match('a', 'abc').group() # 同search,不过在字符串开始处进行匹配
print(ret)
#结果 : 'a'
# re.split()
# 参数:
# pattern,正则表达式, 即以某个正则来拆分
# string, 被拆分的源字符串
# maxsplit=0, 最大拆分次数
# flags=0 标识
# 正则表达式来拆分字符串
strData ="wen1 is2 a3 old4 man5"
lis_re = re.split(" ",strData)
# list_re=re.split("\d",strData)
# list_re=re.split("[0-9]",strData)
# list_re=re.split(" +",strData) #注意,+前面有个空格
# print(listRes) 结果:['wen1', 'is2', 'a3', 'old4', 'man5']
# 结果:['wen', ' is', ' a', ' old', ' man', '']
# 如果想保留用来作切割标准的字符串:只需给它添加分组即可
ret = re.split('\d+','alex83taibai40egon25')
print(ret) # ['alex', 'taibai', 'egon', ''] # 最后一次切割 留下末尾一个空
ret = re.split('(\d+)','alex83taibai40egon25aa')
print(ret) # ['alex', '83', 'taibai', '40', 'egon', '25', 'aa']
# split的优先级查询
ret=re.split("\d+","eva3egon4yuan")
print(ret) #结果 : ['eva', 'egon', 'yuan']
ret=re.split("(\d+)","eva3egon4yuan")
print(ret) # 结果 : ['eva', '3', 'egon', '4', 'yuan']
# 在匹配部分加上()之后所切出的结果是不同的,
# 没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项,
# 这个在某些需要保留匹配部分的使用过程是非常重要的。
str= "蔡徐篮球 是 xxx\n蔡徐坤歌手 是 yyy\n 肖战 是 帅哥"
# re.sub()
# 参数
pattern
repl, 用来替换的新字符串
string
count=0, 替换的次数,默认是全部替换
flags=0 # 不能修改,默认可以不写
# 默认是 re.M 模式,会换行匹配所有
res = re.sub("蔡徐.{,2}","肖战",str)
'''
肖战 是 xxx
肖战手 是 yyy
肖战 是 帅哥
'''
# subn与sub功能一致, 但是 sub是返回替 换后的新字符串,
# subn返回的是元组,元组有2个元素, 元素1代表替换后的新字符串, 元素2代表的替换的次数
res = re.subn("蔡徐.{,2}","肖战",str)
# ('肖战 是 xxx\n肖战手 是 yyy\n 肖战 是 帅哥', 2)
print(res)
正则表达式不仅仅有强大的匹配功能,还有强大提取功能
(xyz) 将xyz看作一个整体
(xyz) 将xyz看成一个小组
re.search() 模式下的分组
import re
s = 'wahaha' # 标签语言 html 网页
ret = re.search('<(\w+)>(\w+)(\w+)>',s)
print(ret.group(0)) # 所有的结果
print(ret.group(1)) # 数字参数代表的是取对应分组中的内容
print(ret.group(2))
print(ret.group(3))
re.findall() 模式的分组
# 为了findall也可以顺利取到分组中的内容,有一个特殊的语法,就是优先显示分组中的内容
ret = re.findall('(\w+)',s)
print(ret) #['a', 'wahaha', 'a']
ret = re.findall('>(\w+)<',s)
print(ret) #['wahaha']
strData = "010-34545546"
numberPattern = "(\d{3})-(\d{8})" 结果:[('010', '34545546')]
numberPattern = "((\d{3})-(\d{8}))" 结果:[('010-34545546', '010', '34545546')]
# findall会将所有的组作为一个元组的元素,放在列表中,组是靠括号来分的
res = re.findall(numberPattern,strData)
print(res) # print(res[0]) 结果:('010', '34545546')
re.findall模式下,取消分组优先
ret = re.findall('\d+(\.\d+)?','1.234*4.3')
print(ret) # 结果是:['.234', '.3']
# 为什么只显示了 小数位,因为findall优先显示分组中的内容
# 取消分组优先做法:在分组内加上?:(?:正则表达式)
ret = re.findall('\d+(?:\.\d+)?','1.234*4.3')
print(ret)
str_date = "010-34545546"
numberPattern = "\d{3}-\d{8}"
numberPattern = "(\d{3})-(\d{8})"
numberPattern = "((\d{3})-(\d{8}))"
numberPattern = "(?P\d{3})-(?P\d{8})"
res = re.match(numberPattern,str_date)
print(res)
# group可以提取完整匹配的字符
# () 可以将字符串分成多个组, 分组顺序是由外到里, 由前到后
print(res.group()) # 默认是0
print(res.group(1)) # 第一组
print(res.group(2)) # 第二组
#print(res.group(3)) # 第三组
>>>可以给每一个分组取一个别名, 格式:(?P<别名>正则)
# 获取的时候可以直接根据别名来获取
print(res.group("First"))
print(res.group("Last"))
# groups 将() 包裹的内容进行分组提取, 将所有的分组作为元组的元素,并作为结果返回
# 它只看括号
res=re.match("\d{3}-\d{8}",strDate) # 这里没括号,输出结果为:()空元祖
print(res.groups())
分组命名的用法
s = 'wahaha' # 加入div标签不一致
pattern = '<(\w+)>(\w+)(\w+)>'
ret = re.search(pattern,s)
# print(ret.group(1) == ret.group(3)) False
# 使用前面的分组 要求使用这个名字的分组和前面同名分组中的内容匹配的必须一致
pattern = '<(?P\w+)>(\w+)(?P=tab)>'
ret = re.search(pattern,s)
print(ret) #False
>>>练习
# 2018-12-06 这种时间格式,中间的分隔符必须一致,所以可以用到命名分组
# 2018.12.6
# 2018 12 06
# 12:30:30
总结
import re
ret = re.findall('a', 'eva egon yuan') # 返回所有满足匹配条件的结果,放在列表里
print(ret) # 结果 : ['a', 'a']
ret = re.search('a', 'eva egon yuan').group()
print(ret) # 结果 : 'a'
# 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
# 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
ret = re.match('a', 'abc').group() # 同search,不过尽在字符串开始处进行匹配
print(ret)
# 结果 : 'a'
ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret) # ['', '', 'cd']
ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1) # 将数字替换成'H',参数1表示只替换1个
print(ret) # evaHegon4yuan4
ret = re.subn('\d', 'H', 'eva3egon4yuan4') # 将数字替换成'H',返回元组(替换的结果,替换了多少次)
print(ret)
obj = re.compile('\d{3}') # 将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
ret = obj.search('abc123eeee') # 正则表达式对象调用search,参数为待匹配的字符串
print(ret.group()) # 结果 : 123
import re
ret = re.finditer('\d', 'ds3sy4784a') # finditer返回一个存放匹配结果的迭代器
print(ret) #
print(next(ret).group()) # 查看第一个结果
print(next(ret).group()) # 查看第二个结果
print([i.group() for i in ret]) # 查看剩余的左右结果
'''
先编译后使用
re模块的进阶 : 时间/空间
compile 节省你使用正则表达式解决问题的时间
编译 正则表达式 编译成 字节码
在多次使用的过程中 不会多次编译
ret = re.compile('\d+') # 已经完成编译了
print(ret)
res = ret.findall('alex83taibai40egon25')
print(res) # 类型是“list”
res = ret.search('sjkhk172按实际花费928')
print(res.group())
finditer 节省你使用正则表达式解决问题的空间/内存
ret = re.finditer('\d+','alex83taibai40egon25')
#print(ret)这是一个迭代器
for i in ret:
print(i.group())
findall 返回列表 找所有的匹配项
search 匹配就 返回一个变量,通过group取匹配到的第一个值,不匹配就返回None,group会报错
match 相当于search的正则表达式中加了一个'^'
spilt 返回列表,按照正则规则切割,默认匹配到的内容会被切掉
sub/subn 替换,按照正则规则去寻找要被替换掉的内容,subn返回元组,第二个值是替换的次数
compile 编译一个正则表达式,用这个结果去search match findall finditer 能够节省时间
finditer 返回一个迭代器,所有的结果都在这个迭代器中,需要通过循环+group的形式取值 能够节省内存
'''
import re
ret = re.search("<(?P\w+)>\w+(?P=tag_name)>" ,"hello
")
# 还可以在分组中利用?的形式给分组起名字
# 获取的匹配结果可以直接用group('名字')拿到对应的值
print(ret.group('tag_name')) # 结果 :h1
print(ret.group()) # 结果 :hello
ret = re.search(r"<(\w+)>\w+\1>","hello
")
# 如果不给组起名字,也可以用\序号来找到对应的组,表示要找的内容和前面的组内容一致
# 获取的匹配结果可以直接用group(序号)拿到对应的值
print(ret.group(1))
print(ret.group()) # 结果 :hello
# 正则匹配所有所有tag
<[^>]+> # [^>]+ 指除了“>”外,但是至少有一个字符
# 匹配 open tag
<[^/][^>]*>
# 匹配 close tag
</[^>]+>
# 匹配 单标签
<[^>]+/>
import re
s=""
ret=re.search("<[^>]+>",s).group()
print(ret) #
# 匹配 双/单 引号
"[^"]*" # 双/单 引号 中可以是没有任何内容
爬虫实例:爬取豆瓣排名前250的电影
import re
from urllib.request import urlopen
def getPage(url): # 获取网页的字符串
response = urlopen(url)
return response.read().decode('utf-8')
def parsePage(s):
ret = com.finditer(s) # 从s这个网页源码中 找到所有符合com正则表达式规则的内容 并且以迭代器的形式返回
for i in ret:
yield {
"id": i.group("id"),
"title": i.group("title"),
"rating_num": i.group("rating_num"),
"comment_num": i.group("comment_num"),
}
def main(num): # 0 25 50 #这个函数执行10次,每次爬取一页的内容
url = 'https://movie.douban.com/top250?start=%s&filter=' % num #每次等于多少页
response_html = getPage(url) # response_html就是这个url对应的html代码 就是 str
ret = parsePage(response_html) # ret是一个生成器
#print(ret)
f = open("move_info7", "a", encoding="utf8")
for obj in ret:
print(obj)
data = str(obj)
f.write(data + "\n")
f.close()
com = re.compile(
'.*?.*?(?P\d+).*?(?P.*?) '
'.*?(?P.*?)评价 ' .*?, re.S)
#.*? 的使用,遇到什么停止,加上 re.S模式;把要用的内容分组好
count = 0
for i in range(10):
main(count)
count += 25
对照标签
匹配整数
import re
ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) # ['1', '2', '60', '40', '35', '5', '4', '3']
ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret) # ['1', '-2', '60', '', '5', '-4', '3']
ret.remove("")
print(ret) # ['1', '-2', '60', '5', '-4', '3']
匹配一个整数:\d+
匹配一个小数:\d+\.\d+
匹配一个整数或者小数:\d+\.\d+ | \d+ 或者 \d+(\.\d+)?
数字匹配
>>>匹配一段文本中的每行的邮箱
# 只允许英文字母、数字、下划线、英文句号、以及中划线组成
# 例:zhoujielun-001@gmail.com
^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
# 名称允许汉字、字母、数字,域名只允许英文域名
# 例:周杰伦001Abc@lenovo.com.cn
^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
# 邮箱规则:
# @之前必须有内容且只能是字母(大小写)、数字、下划线(_)、减号(-)、点(.)
# @和最后一个点(.)之间必须有内容且只能是字母(大小写)、数字、点(.)、减号(-),
# --且两个点不能挨着
# 最后一个点(.)之后必须有内容且内容只能是字母(大小写)、数字且长度为大于等于2个字
# --节,小于等于6个字节
[0-9a-zA-Z][\w\-.]+@[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*\.[A-Za-z0-9]{2,6}
>>>匹配一段文本中的每行的时间字符串,比如:‘1990-07-12’;
分别取出1年的12个月(^(0?[1-9]|1[0-2])$)、
一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$
>>>匹配qq号。(腾讯QQ号从10000开始) [1,9][0,9]{4,}
>>>匹配一个浮点数。 ^(-?\d+)(\.\d+)?$ 或者 -?\d+\.?\d*
>>>匹配汉字。 ^[\u4e00-\u9fa5]{0,}$
>>>匹配出所有整数
>>> 1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))
# 从上面算式中匹配出内层没有其他小括号的表达式
\([^()]+\)
>>>从类似9-1000/3+7/3*99/4*2998+10*568/14的表达式中匹配出从左到右第一个乘法或除法
\d+[*/]\d+
模拟一个计算器功能
import re
def atmo_cal(exp): # 原子计算器,最小单位不可再拆分了,处理乘除法,如:5*0.6;6/2
if "*" in exp:
a, b = exp.split("*") # 最小了,只有两位
return str(float(a) * float(b))
elif "/" in exp:
a, b = exp.split("/")
return str(float(a) / float(b))
def add_sub(exp): # 计算加减法
ret = re.findall("[-+]?\d+(?:\.\d+)?", exp) # 取消分组优先,得到一个列表
exp_sum = 0
for i in ret:
exp_sum += float(i)
return exp_sum
def mul_div(exp): # 计算乘除法
while True:
ret = re.search("\d+(?:\.\d+)?[/*]-?\d+(?:\.\d+)?", exp)
# 用的是search,所以从左往右匹配到第一项就返回结果
if ret:
atmo_exp = ret.group() # 分组取出原子每一项
res = atmo_cal(atmo_exp) # 调用原子计算器,打印结果为"-22.0"
# "2-1*-22+3-4/-5" "-22.0" 替换原式子中的位置即:"2--22.0-3-4/-5"
exp = exp.replace(atmo_exp, res) # 参数位置 旧的,新的 将旧的替换成新的
else: # 2--22.0-3--0.8 乘除法都运算完毕了
return exp
def format(exp):
exp = exp.replace('--', '+')
exp = exp.replace('+-', '-')
exp = exp.replace('-+', '-')
exp = exp.replace('++', '+')
return exp
def cal(exp): # 将乘法或者除法匹配出来;"2-1*-22-3-4/-5" 用来计算用的
exp = mul_div(exp) # 调用乘除法函数
exp = format(exp) # 调用格式化函数后:2+22.0-3+0.8
exp_sum = add_sub(exp) # 调用计算加法的函数 21.8
return exp_sum # 返回结果
# print(cal("2-1*-22-3-4/-5"))
def main(exp): # 主函数
exp = exp.replace(" ", "") # 去掉用户输入内容的空格
while True:
ret = re.search("\([^()]+\)", exp) # (-40/5)
if ret:
inner_bracket = ret.group() # 这里可能得到各种运算
res = str(cal(inner_bracket))
# print(inner_bracket,type(res))
exp = exp.replace(inner_bracket, res)
# print(exp) 再格式化下 1-2*((60-30+-8.0*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))
exp = format(exp)
# print(exp)
else:
break
# print(exp,type(exp)) ;1-2*-1388335.8476190479
return cal(exp)
s = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
ret = main(s)
print(ret, type(ret))
print(eval(s), type(eval(s))) # 用户可能再用到,所以像eval一样返回原来的格式
# 2776672.6952380957
# 2776672.6952380957