继上篇文章说了正则表达式的基础用法,那今天我们就继续说一下正则表达式的复杂的用法。好了,废话不多说,直接进入正题。
情景:当你想要匹配一个qq号,qq号码长度为5-10位,那根据上篇文章的说法,很容易就可以想到该正则:
[0-9]{5,10}
这样是可以的,但是当你匹配一个长度大于10的号码时就会出错,这时就会去该字符串的前10个数字出来,如下:
import re
a='1234567890'
r=re.findall('[0-9]{5,10}',a)#明显当查找的字符串长度大于8位时就会出错,只会截取前一部分长度
print(r)
结果:
['1234567890']
这样的话你就会得到一个错误的qq号码。
这时就需要引入边界匹配了:
现在再写个匹配qq号码的正则:
r=re.findall('^[0-9]{5,10}$',a)#这个表示从左边起为5-10的数字长度,右边也是一样
print('第一个匹配结果:',r)
a = '1234567890'
r=re.findall('^[0-9]{5,10}$',a)
print('第二个匹配结果:',r)
结果:
第一个匹配结果: []
第二个匹配结果: ['1234567890']
这样就可以匹配到了,是不是很神奇?
组:前面我们有用 [ ] 来匹配,中括号里面表示的是或关系,而这里的组表示的是并关系,并且用小括号括起来 ( )。
比如:重复 python 字样三次
import re
a='pythonpythonpythonjakjpythonpythonsdjjpythonpythonpythonsd'
r=re.findall('(python){3}',a)
print(r)
结果:
['python', 'python']
这里的结果不是返回三个python,而是返回这个组,当符合一次就会将此组添加到返回列表中一次。
这个组还挺好用的,再看下这个需求:获取下列英文中的life和python之间的内容。
a='life is short,i use python'
r=re.findall('life(.*)python',a,re.S)
print(r) # 这样获取的就是组内的内容
结果:
[' is short,i use ']
这个组还常用,因为在我们经常在用正则来解析html元素时,经常需要获取两个标签之间的内容,标签是确定的,标签内容不确定,就可以用这个了。如下这个html元素:
<strong><a href="#py2">python进阶 a>
<a href="#python3">python入门 a>
<a href="#vce">vce解决方法 a>
<a href="demo06.html#new" target="_blank">百度 a>
<a href="mailto: [email protected]">反馈意见a>
<a href="img/1.jpg">下载图片 a>
strong>
这样就可以用组来获取a标签的内容了:(.*?)
。?表示非贪婪哦!
当需要写多个匹配模式时,可以用 | 分隔每个模式
代码如下:
a='Java12Python89'
r=re.findall('python',a,re.I)
print(r)
a='hsjhj h123jfkksf hajkGH\nkj fjfk'
r=re.findall('.',a,re.I|re.S)
print(r)
结果:
['Python']
['h', 's', 'j', 'h', 'j', ' ', 'h', '1', '2', '3', 'j', 'f', 'k', 'k', 's', 'f', ' ', 'h', 'a', 'j', 'k', 'G', 'H', '\n', 'k', 'j', ' ', 'f', 'j', 'f', 'k']
正则除了可以用来检索字符串,还可以用来替换字符串,常见的可以用来替换那些文本中的空格,制表符和回车等,这些都是用一个正则就可以搞定的了。
python中用这个方法来进行正则替换
re.sub(pattern, repl, string, count=0, flags=0)
代码如下:
import re
a='skjC#ksjfc#jkdsc#'
r=re.sub('c#','gg',a)#返回值是替换后的字符串
print(r)
print(a)
r=re.sub('c#','gg',a,1) # 这个加了替换次数
print(r)
r=re.sub('c#','gg',a,1,re.I) # 加了匹配模式,忽视大小写
print(r)
结果
skjC#ksjfggjkdsgg
skjC#ksjfggjkdsc#
skjggksjfc#jkdsc#
我们试试第二个参数为函数的情况
def convert(value):#他是把对象传进去这个参数
print(value)
#可以通过group()方法来获取内容
return '!!'+value.group()+"!!"
r=re.sub('c#',convert,a,flags=re.I)#接收个参数后,更改后的内容为他的返回值
print(r)
结果
<_sre.SRE_Match object; span=(3, 5), match='C#'>
<_sre.SRE_Match object; span=(9, 11), match='c#'>
<_sre.SRE_Match object; span=(15, 17), match='c#'>
skj!!C#!!ksjf!!c#!!jkds!!c#!!
这个第二个参数为convert函数,里面的.group() 方法是获取匹配后的字符串的值,所以我们就可以根据匹配后的字符串来进行相对应的替换内容,比如这个简单的小需求:
把字符串中的数字大于50的改为99,小于的就改为11。
a='ds+45sd78asd12568asd45asd74ew+9ddf12sd45'
def func(value):
if int(value.group())>50:
return '99'
else:
return '11'
r=re.sub('\d{1,2}',func,a)
print(r)
结果
ds+11sd99asd119911asd11asd99ew+11ddf11sd11
另谈两个函数
代码:
import re
a='pythonphpjavacphp'
r=re.match('php',a)#这个一开始没有就返回None
print(r)
r=re.search('php',a)#这个搜索到之后就返回一个对象
#返回的对象可以通过group()方法来获取他的内容
print(r)
# 获取匹配内容
print(r.group())
结果
None
<_sre.SRE_Match object; span=(6, 9), match='php'>
php
这两个函数返回的内容的几个属性:
前面提到组的概念,试下这两个方法的组的用法:
import re
#获取life和python之间的内容
a='life is short,i use python'
r=re.search('life(.*)python',a,re.S)#用小括号的就是一组
print(r.group(1))#这个下标1就是对应的中间部分
#也可以获取中间的两部分
a='javawoshipythonjunephp'
r=re.search('java(.*)python(.*)php',a)#两个小括号就是分成了两组
print(r.group(1),r.group(2))#分别打印第一第二组
print(r.groups())#这个获取所有分组信息
结果:
is short,i use
woshi june
('woshi', 'june')
上面的代码注释已经很清楚了,还有个group()方法是获取整个正则匹配的内容,不按分组。match()方法也一样,就不演示了。
最后一个问题:怎样拆分含有多种分隔符的字符串?
比如:kfs;hsji’fhsikf*bhsfk=jsf/shj。要将不属于字母的都去掉,你是不是会想到用字符串的循环,然后再一个一个分割出来?我告诉你,学了正则之后,再也不用这么麻烦了。re库里面有个split()方法,如下:
re.split(pattern, string, maxsplit=0),参数看名字应该就能知道。直接一行代码进行分割:
a = 'kfs;hsjifhsikf*bhsfk=jsf/shj'
r = re.split('[;*=/]', a)
print(r)
结果:
['kfs', 'hsjifhsikf', 'bhsfk', 'jsf', 'shj']
是不是很完美?所以说正则必须得学!
这个正则复杂点的已经说完了,还有些进阶的,不过暂时没有用到,就先不写了,有需要的可以百度。
正则表达式相关的jupyter的笔记已经上传的到我的GitHub上,有需要的可以下载下来看看,给个star也是可以的。
GitHub:https://github.com/ldz0/Python-jupyter-notebooks