re模块
re模块
正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re
常用正则表达式规则
'.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$' 匹配字符结尾, 若指定flags MULTILINE ,re.search('foo.$','foo1\nfoo2\n',re.MULTILINE).group() 会匹配到foo1
'*' 匹配*号前的字符0次或多次, re.search('a*','aaaabac') 结果'aaaa'
'+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?' 匹配前一个字符1次或0次 ,re.search('b?','alex').group() 匹配b 0次
'{m}' 匹配前一个字符m次 ,re.search('b{3}','alexbbbs').group() 匹配到'bbb'
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配, re.search("(abc){2}a(123|45)", "abcabca456c").group() 结果为'abcabca45'
'\A' 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的,相当于re.match('abc',"alexabc") 或^
'\Z' 匹配字符结尾,同$
'\d' 匹配数字0-9
'\D' 匹配非数字
'\w' 匹配[A-Za-z0-9]
'\W' 匹配非[A-Za-z0-9]
'\s' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'
'(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}
例子:
#\W与\w
a="hello 123_12?"
print(re.findall('\W',a))#匹配非字母数字与下划线 打印结果:[' ', '?']
print(re.findall('\w',a))#匹配字母数字与下线划 打印结果:['h', 'e', 'l', 'l', 'o', '1', '2', '3', '_', '1', '2']
#\S与\s
a="hello \t123_12\n"
print(re.findall('\s',a))#匹配任意空字符,等价于[\n\t\r\f]都可以为\s匹配,打印结果:[' ', '\t', '\n']
print(re.findall('\S',a))#匹配任意非空字符 打印结果:['h', 'e', 'l', 'l', 'o', '1', '2', '3', '_', '1', '2']
#\n与\t
print(re.findall(r'\n',"hello\n 123_12\t"))#匹配一个换行符 打印结果:['\n']
print(re.findall(r'\t',"hello \n123_12\t"))#匹配一个制表符 打印结果:['\t']
#\d与\D
print(re.findall('\d','hello 123'))#匹配任意数字,等价[0-9] 打印结果:['1', '2', '3']
print(re.findall('\D','hello,123'))#匹配任意非数字 打印结果:['h', 'e', 'l', 'l', 'o', ',']
#\A与\Z
print(re.findall('\Ahe','hello 123'))#匹配字符串开始位置,相当于^ 打印结果:['he']
print(re.findall('3\Z','hello,123'))#匹配字符串结束位置,相当于$ 打印结果:['3']
#^与$
print(re.findall('^he','hello 123'))#匹配字符串开始位置 打印结果:['he']
print(re.findall('123$','hello,123'))#匹配字符串结束位置 打印结果:['123']
#.匹配任意一个字符,除了换行符
print(re.findall('h.l','hello 123'))#打印结果:['hel']
print(re.findall('a.b','a1b a*b a b aaab'))#打印结果:['a1b', 'a*b', 'a b', 'aab']
print(re.findall('a.b','a1b a*b a\nb aaab'))#打印结果:['a1b', 'a*b', 'aab']
#* 匹配0个或者多个字符
print(re.findall('ab*','bbbbbb'))#打印结果:[]
print(re.findall('ab*','a'))#打印结果:['a']
print(re.findall('ab*','abbbbbbbbb'))#打印结果:['abbbbbbbbb']
#?匹配0个或者1个任意字符
print(re.findall('ab?','a'))#打印结果:['a']
print(re.findall('ab?','abbb'))#打印结果:['ab']
#匹配小数点在内的任意数字
print(re.findall('\d+\.?\d*',"asdfasdf12123.3as1.133342dfa12adsf1asdf3"))#打印结果:['12123.3', '1.133342', '12', '1', '3']
#.* 默认为贪婪匹配
print(re.findall('a.*e','acddedddeb'))#打印结果:['acddeddde']
#.*? 非贪婪匹配
print(re.findall('a.*?e','acddedddeb'))#打印结果:['acdde']
#+ 匹配一个或者多个表达式
print(re.findall('ab+','abbbbbbb1231231'))#['abbbbbbb']
#{n,m} 匹配n到m次
print(re.findall('ab{2}','abbb'))#['abb']
print(re.findall('ab{2,4}','abbb'))#['abbb']
print(re.findall('ab{1,}','abbb'))#['abbb'] 相当于ab+
print(re.findall('ab{0,}','abbb'))#['abbb'] 相当于ab*
#[] 代表或的意思
print(re.findall('a[1*-]b','a1b a*b a-b'))#[]内的都为普通字符了,且如果-没有被转意的话,应该放到[]的开头或结尾
print(re.findall('a[^1*-]b','a1b a*b a-b a=b'))#[]内的^代表的意思是取反,所以结果为['a=b']
print(re.findall('a[^0-9]b','a1b a*b a-b a=b'))#[]内的^代表的意思是取反,所以结果为['a=b']
#\ 转义
# 1 后面加一个元字符使其变成普通符号 \. \*
# 2 将一些普通符号变成特殊符号 比如 \d \w
# print(re.findall('a\\c','a\c'))#对于正则来说a\\c确实可以匹配到a\c,
但是在python解释器读取a\\c时,会发生转义,然后交给re去执行,所以抛出异常
print(re.findall(r'a\\c','a\c'))#r代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义
#()分组:
print(re.findall('ab+','abababab123'))#['ab', 'ab', 'ab', 'ab']
print(re.findall('(ab)123','ababab123saefqwef')) #['ab'],匹配到末尾的ab123中的ab
print(re.findall('(?:ab)+123','ababab123'))#findall的结果不是匹配的全部内容,而是组内的内容,?:可以让结果为匹配的全部内容
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">点击</a>'))#['http://www.baidu.com']
#命名分组 格式:?P<"组名">
s2=re.search(r'(?P<author>\w+)\\articles\\(?P<id>\d{7}\.\w+)',r'linhaifeng\articles\6384466.html')
print(s2.group("id"))#6384466.html
print(s2.group("author"))#linhaifeng
#| 或
print(re.findall('www\.(?:baidu|oldboy)\.com','www.baidu.com'))#['www.baidu.com']
re的匹配语法有以下几种
- re.match 从头开始匹配
- re.search 匹配包含
- re.findall 把所有匹配到的字符放到以列表中的元素返回
- re.split 以匹配到的字符当做列表分隔符
- re.sub 匹配字符并替换
- re.fullmatch 全部匹配
例子:
#re的方法
print(re.findall("\d+",'12312312adfadf'))#匹配所有
#search:只匹配第一个结果
s1=re.search('12','aad1231412')#只到找到第一个匹配然后返回一个包含匹配信息的对象, 该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
print(s1.group())#['12312312']
#match:只在开始字符进行匹配
s2=re.match('a','aa123121a23a3212a')#None,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match
print(s2.group())#a
#split:分割
print(re.split('1','1231211'))#['', '23', '2', '', ''] 先按1进行分割,得到'','231211',再对'231211'进行割,以此类推
#sub 替换
print(re.sub('\d+','A','hello aadfw1231a312a'))#hello aadfwAaAa 替换所以数字换成A
print(re.sub('\d+','A','hello 123adf3212',2))#hello AadfA 替换两次
#compile:编释方法
a=re.compile("\d+")#定制自己的查询方法
print(a.findall('adfasdf12312adfd1231adf123'))#['12312', '1231', '123']
练习
- 匹配合法的手机号
import re
def phone(num):
'匹配手机号是否合法'
if re.findall('^(1\d\d)\d{8}$',num):
print('%s这是正确的手机号码'%num)
else:
print('%s这不是正确的手机号码'%num)
return (num)
phone('18221212817a')
- 匹配合法的邮箱
import re
def _email(email):
'匹配邮箱'
if re.findall('[\w-]+[.\w-]+@[.\w]+[a-z]{2,3}',email):
print('%s此邮箱是正确的'%email)
else:
print('%s此邮箱是错误的'%email)
_email('www@163.cn')
计算器:
import re
def compute_mul_div(arg):
'''
操作乘除
:param expression:表达式
:return:计算结果
1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )
'''
val=arg[0]
pattern=re.compile(r'\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*')
# mch=re.search('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*',val)
mch=pattern.search(val)
if not mch:
return
# content=re.search('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*',val).group()
content=pattern.search(val).group()
if len(content.split('*')) >1:
n1,n2=content.split('*')
value=float(n1)*float(n2)
else:
n1,n2=content.split('/')
value=float(n1) / float(n2)
# before,after=re.split('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*',val,1)
before,after=pattern.split(val,1)
new_str='%s%s%s' %(before,value,after)
arg[0]=new_str
compute_mul_div(arg)
def compute_add_sub(arg):
'''
操作加减
:param expression: 表达式
:return: 计算结果
'''
while True:
if arg[0].__contains__('+-') or arg[0].__contains__('++') or arg[0].__contains__('-+') or arg[0].__contains__('--'):
arg[0]=arg[0].replace('+-','-')
arg[0]=arg[0].replace('++','+')
arg[0] = arg[0].replace('--', '+')
arg[0] = arg[0].replace('-+', '-')
else:
break
if arg[0].startswith('-'):
arg[1] += 1
arg[0]=arg[0].replace('-','&')
arg[0] = arg[0].replace('+', '-')
arg[0] = arg[0].replace('&', '+')
arg[0]=arg[0][1:]
val=arg[0]
pattern=re.compile('\d+\.*\d*[\+\-]{1}\d+\.*\d*')
# mch=re.search('\d+\.*\d*[\+\-]{1}\d+\.*\d*',val)
mch=pattern.search(val)
if not mch:
return
content=pattern.search(val).group()
if len(content.split('+')) >1:
n1,n2=content.split('+')
value=float(n1) + float(n2)
else:
n1,n2=content.split('-')
value=float(n1) - float(n2)
before,after=pattern.split(val,1)
new_str='%s%s%s' %(before,value,after)
arg[0]=new_str
compute_add_sub(arg)
def compute(expression):
'''
操作加减乘除
:param expression:表达式
:return:计算结果
'''
inp=[expression,0]
#处理表达式中的乘除
compute_mul_div(inp)
#处理
compute_add_sub(inp)
if divmod(inp[1],2)[1] == 1:
result=float(inp[0])
result=result * - 1
else:
result=float(inp[0])
return result
def exec_bracket(expression):
'''
递归处理括号,并计算
:param expression: 表达式
:return: 最终计算结果
'''
pattern=re.compile(r'\(([\+\-\*\/]*\d+\.*\d*){2,}\)')
#如果表达式中已经没有括号,则直接调用负责计算的函数
if not pattern.search(expression):
final=compute(expression)
return final
#获取第一个只含有 数字/小数 和操作符的括号
# 如:
# ['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
# 找出:(-40.0/5)
content=pattern.search(expression).group()
#分割表达式,即:
# 将['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
# 分割更三部分:['1-2*((60-30+( (-40.0/5) *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
berfore,nothing,after=pattern.split(expression,1)
print('before',expression)
content=content[1:len(content)-1]
# 计算,提取的表示 (-40.0/5),并活的结果,即:-40.0/5=-8.0
ret=compute(content)
print('%s=%s' %(content,ret))
# 将执行结果拼接,['1-2*((60-30+( -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
expression="%s%s%s" %(berfore,ret,after)
print('after',expression)
print('=' *10,'上一次计算结束','='*10)
# 循环继续下次括号处理操作,本次携带者的是已被处理后的表达式,即:
# ['1-2*((60-30+ -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']
# 如此周而复始的操作,直到表达式中不再含有括号
return exec_bracket(expression)
# 使用 __name__ 的目的:
# 只有执行 python index.py 时,以下代码才执行
# 如果其他人导入该模块,以下代码不执行
if __name__=='__main__':
#print '*'*20,"请计算表达式:", "1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )" ,'*'*20
#inpp = '1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) '
print("==============================")
print("\t\t\033[31m欢迎使用计算器\033[0m")
print("==============================")
while True:
inpp=input("请输入表达式:|(退出:q)")
inpp=re.sub('\s*','',inpp)
if inpp == "q":
exit()
elif re.search('[^0-9\.\-\+\*\/\%\/\/\*\*\(\)]',inpp):
print("请输入正确的表达式!")
else:
#表达式保存在列表中
result=exec_bracket(inpp)
print("最后的结果是:\033[31m%s\033[0m" %round(result,2))