Python正则匹配必须掌握的10个函数
共 8486字,需浏览 17分钟
·
2021-11-20 02:18
正则表达re模块共有12个函数,我将分类进行讲解,这样方便记忆与理解,先来看看概览:
search、match、fullmatch:查找一个匹配项
findall、finditer:查找多个匹配项
split:分割
sub,subn:替换
compile函数、template函数: 将正则表达式的样式编译为一个 正则表达式对象
print(dir(re))
[...... 'compile', 'copyreg', 'enum', 'error', 'escape', 'findall',
'finditer', 'fullmatch', 'functools', 'match', 'purge', 'search',
'split', 'sre_compile', 'sre_parse', 'sub', 'subn', 'template']
一、查找一个匹配项
查找并返回一个匹配项的函数有3个:search、match、fullmatch,他们的作用分别是:
search:查找任意位置的匹配项
match:必须从字符串开头匹配
fullmatch:整个字符串与正则完全匹配
1) search()
描述:在给定字符串中寻找第一个匹配正则表达式的子字符串,如果找到会返回一个Match对象,这个对象中的元素可以group()得到(之后将会介绍group的概念),如果没找到就会返回None。调用re.match,re.search方法或者对re.finditer结果进行遍历,输出的结果都是re.Match对象
语法:re.search(pattern, string, flags=0)
pattern 匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式
re.search(r"(\w)(.\d)","as.21").group()
's.2'
假设返回的Match对象为m,m.group()来取某组的信息,group(1)返回与第一个子模式匹配的单个字符串,group(2)等等以此类推,start()方法得到对应组的开始索引,end()得到对应组的结束索引,span()以元组形式给出对应组的开始和结束位置,括号中填入组号,不填入组号时默认为0。
匹配对象m方法有很多,几个常用的方法如下:
m.start() 返回匹配到的字符串的起使字符在原始字符串的索引
m.end() 返回匹配到的字符串的结尾字符在原始字符串的索引
m.group() 返回指定组的匹配结果
m.groups() 返回所有组的匹配结果
m.span() 以元组形式给出对应组的开始和结束位置
其中的组,是指用()括号括起来的匹配到的对象,比如下列中的"(\w)(.\d)",就是两个组,第一个组匹配字母,第二个组匹配.+一个数字。
m = re.search(r"(\w)(.\d)","as.21")
m
1 , 4), match='s.2'>
m.group()
s.2
m.group(0)
's.2'
m.group(1) #根据要求返回特定子组
's'
m.group(2)
'.2'
m.start()
1
m.start(2)#第二个组匹配的索引位置
2
m.groups()
('s', '.21')
m.span()
(1, 5)
2) match()
描述:必须从字符串开头匹配,同样返回的是Match对象,对应的方法与search方法一致,此处不再赘述。
语法:re.match(pattern, string, flags=0)
pattern 匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等
#从头开始匹配,返回一个数字,发现报错,无法返回,因为不是数字开头的
text = 'chi13putao14butu520putaopi666'
pattern = r'\d+'
re.match(pattern,text).group()
AttributeError: 'NoneType' object has no attribute 'group'
#从头开始匹配,返回一个单词,正常返回了开头的单词
text = 'chi13putao14butu520putaopi666'
pattern = r'[a-z]+'
re.match(pattern,text).group()
'chi'
3) fullmatch()
描述:整个字符串与正则完全匹配,同样返回的是Match对象,对应的方法与search方法一致,此处不再赘述
语法:(pattern, string, flags=0)
pattern 匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等
#必须要全部符合条件才能匹配
re.fullmatch(r'[a-z]+','chiputao14').group()
AttributeError: 'NoneType' object has no attribute 'group'
re.fullmatch(r'[a-z]+','chiputao').group()
'chiputao'
二、查找多个匹配项
讲完查找一项,现在来看看查找多项吧,查找多项函数主要有:findall函数 与 finditer函数:
1)findall: 从字符串任意位置查找,返回一个列表
2)finditer:从字符串任意位置查找,返回一个迭代器
两个函数功能基本类似,只不过一个是返回列表,一个是返回迭代器。我们知道列表是一次性生成在内存中,而迭代器是需要使用时一点一点生成出来的,运行时占用内存更小。如果存在大量的匹配项的话,建议使用finditer函数,一般情况使两个函数不会有太大的区别。
1)findall
描述:返回字符串里所有不重叠的模式串匹配,以字符串列表的形式出现。
语法:re.findall(pattern, string, flags=0)
pattern 匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等
import re
text = 'Python太强大,我爱学Python'
pattern = 'Python'
re.findall(pattern,text)
['Python', 'Python']
#找出下列字符串中的数字
text = 'chi13putao14butu520putaopi666'
#\d+表示匹配任意数字
pattern = r'\d+'
re.findall(pattern,text)
['13', '14', '666', '520']
text = 'ab-abc-a-cccc-d-aabbcc'
pattern = 'ab*'
re.findall(pattern,text)
['ab', 'ab', 'a', 'a', 'abb']
#找到所有副词
'''findall() 匹配样式 所有 的出现,不仅是像 search() 中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()'''
text = "He was carefully disguised but captured quickly by police."
re.findall(r"\w+ly", text)
['carefully', 'quickly']
2)finditer()
描述:返回一个产生匹配对象实体的迭代器,能产生字符串中所有RE模式串的非重叠匹配。
语法:re.finditer(pattern, string, flags=0)
pattern 匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等
m = re.finditer("Python","Python非常强大,我爱学习Python")
m
for i in m:
print(i.group(0))
Python
Python
#找到所有副词和位置
'''如果需要匹配样式的更多信息, finditer() 可以起到作用,它提供了 匹配对象 作为返回值,而不是字符串。继续上面的例子,如果一个作者希望找到所有副词和它的位置,可以按照下面方法使用 finditer()'''
text = "He was carefully disguised but captured quickly by police."
for m in re.finditer(r"\w+ly", text):
print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quic
三、匹配项分割
1)split()
描述:split能够按照所能匹配的字串将字符串进行切分,返回切分后的字符串列表
形式.切割功能非常强大
语法:re.split(pattern, string, maxsplit=0, flags=0)
pattern:匹配的字符串
string:需要切分的字符串
maxsplit:分隔次数,默认为0(即不限次数)
flags:标志位,用于控制正则表达式的匹配方式,flags表示模式,就是上面我们讲解的常量!支持正则及多个字符切割。
正则表达的分割函数与字符串的分割函数一样,都是split,只是前面的模块不一样,正则表达的函数为, ,用pattern分开string,maxsplit表示最多进行分割次数,
re.split(r";","var A;B;C:integer;")
['var A', 'B', 'C:integer', '']
re.split(r"[;:]","var A;B;C:integer;")
['var A', 'B', 'C', 'integer', '']
text = 'chi13putao14butu520putaopi666'
pattern = r'\d+'
re.split(pattern,text)
['chi', 'putao', 'butu', 'putaopi', '']
line = 'aac bba ccd;dde eef,fff'
#单字符切割
re.split(r';',line)
['aac bba ccd', 'dde eef,fff']
#两个字符以上切割需要放在 [ ] 中
re.split(r'[;,]',line)
['aac bba ccd', 'dde eef', 'fff']
#所有空白字符切割
re.split(r'[;,\s]',line)
['aac', 'bba', 'ccd', 'dde', '', '', 'eef', 'fff']
#使用括号捕获分组,默认保留分割符
re.split(r'([;])',line)
['aac bba ccd', ';', 'dde eef,fff']
#不想保留分隔符,以(?:...)的形式指定
re.split(r'(?:[;])',line)
['aac bba ccd', 'dde eef,fff']
#不想保留分隔符,以(?:...)的形式指定
re.split(r'(?:[;,\s])',line)
['aac', 'bba', 'ccd', 'dde', '', '', 'eef', 'fff']
注意:str模块也有一个split函数 ,那这两个函数该怎么选呢?str.split函数功能简单,不支持正则分割,而re.split支持正则。关于二者的速度如何?来实际测试一下,在相同数据量的情况下使用re.split函数与str.split函数执行次数 与 执行时间 对比图:
#运行时间统计 只计算了程序运行的CPU时间,且每次测试时间有所差异
import time
#统计次数
n = 1000
``start_t = time.perf_counter()
for i in range(n):
re.split(r';',line)##re.split
end_t = time.perf_counter()
print ('re.split: '+str(round(1000000*(end_t-start_t),2)))
start_t = time.perf_counter()
for i in range(n):
line.split(';')##str.split
end_t = time.perf_counter()
print ('str.split: '+str(round(1000000*(end_t-start_t),2)))
通过上图对比发现,5000次循环以内str.split函数和re.split函数执行时间差异不大,而循环次数1000次以上后str.split函数明显更快,而且次数越多差距越大!
所以结论是,在不需要正则支持的情况下使用str.split函数更合适,反之则使用re.split函数。具体执行时间与测试数据有关!且每次测试时间有所差异
四、匹配项替换
替换主要有sub函数与subn函数两个函数,他们功能类似,不同点在于sub只返回替换后的字符串,subn返回一个元组,包含替换后的字符串和替换次数。
python 里面可以用 replace 实现简单的替换字符串操作,如果要实现复杂一点的替换字符串操作,需用到正则表达式。
re.sub用于替换字符串中匹配项,返回一个替换后的字符串,subn方法与sub()相同, 但返回一个元组, 其中包含新字符串和替换次数。
sub是substitute表示替换。
1)sub
描述:它的作用是正则替换。
语法:re.sub(pattern, repl, string, count=0, flags=0)
pattern:该参数表示正则中的模式字符串;
repl:repl可以是字符串,也可以是可调用的函数对象;如果是字符串,则处理其中的反斜杠转义。如果它是可调用的函数对象,则传递match对象,并且必须返回要使用的替换字符串
string:该参数表示要被处理(查找替换)的原始字符串;
count:可选参数,表示是要替换的最大次数,而且必须是非负整数,该参数默认为0,即所有的匹配都会被替换;
flags:可选参数,表示编译时用的匹配模式(如忽略大小写、多行模式等),数字形式,默认为0。
把字符串 aaa34bvsa56s中的数字替换为 *号
import re
re.sub('\d+','*','aaa34bvsa56s')#连续数字替换
'aaa*bvsa*s'
re.sub('\d','*','aaa34bvsa56s')#每个数字都替换一次
'aaa**bvsa**s'
#只天换一次count=1,第二次的数字没有被替换
re.sub('\d+','*','aaa34bvsa56s',count=1)
'aaa*bvsa56s'
把chi13putao14butu520putaopi666中的数字换成...
text = 'chi13putao14butu520putaopi666'
pattern = r'\d+'
re.sub(pattern,'...',text)
'chi...putao...butu...putaopi...'
关于第二个参数的用法,我们可以看看下面的内容
#定义一个函数
def refun(repl):
print(type(repl))
return('...')
re.sub('\d+',refun,'aaa34bvsa56s')
'aaa...bvsa...s'
从上面的例子看来,似乎没啥区别
原字符串中有多少项被匹配到,这个函数就会被调用几次。
至于传进来的这个match对象,我们调用它的.group()方法,就能获取到被匹配到的内容,如下所示:
def refun(repl):
print(type(repl),repl.group())
return('...')
re.sub('\d+',refun,'aaa34bvsa56s')
34
56 Out[113]: 'aaa...bvsa...s'
这个功能有什么用呢?我们设想有一个字符串moblie18123456794num123,这个字符串中有两段数字,并且长短是不一样的。第一个数字是11位的手机号。我想把字符串替换为:moblie[隐藏手机号]num***。不是手机号的数字,每一位数字逐位替换为星号。
def refun(repl):
if len(repl.group()) == 11:
return '[隐藏手机号]'
else:
return '*' * len(repl.group())
re.sub('\d+', refun, 'moblie18217052373num123')
'moblie[隐藏手机号]num***'
2)subn
描述:函数与 re.sub函数 功能一致,只不过返回一个元组 (字符串, 替换次数)。
语法:re.subn(pattern, repl, string, count=0, flags=0)
参数:同re.sub
re.subn('\d+','*','aaa34bvsa56s')#连续数字替换
('aaa*bvsa*s', 2)
re.subn('\d','*','aaa34bvsa56s')#每个数字都替换一次
('aaa**bvsa**s', 4)
text = 'chi13putao14butu520putaopi666'
pattern = r'\d+'
re.subn(pattern,'...',text)
('chi...putao...butu...putaopi...', 4)
五、编译正则对象
描述:将正则表达式的样式编译为一个正则表达式对象(正则对象),可以用于匹配
语法:re.compile(pattern, flags=0)
参数:
pattern:该参数表示正则中的模式字符串;
flags:可选参数,表示编译时用的匹配模式(如忽略大小写、多行模式等),数字形式,默认为0。
prog = re.compile(pattern)
result = prog.match(string)
等价于
result = re.match(pattern, string)
如果需要多次使用这个正则表达式的话,使用re.compile()和保存这个正则对象以便复用,可以让程序更加高效。
六、转义函数
re.escape(pattern) 可以转义正则表达式中具有特殊含义的字符,比如:. 或者 * re.escape(pattern) 看似非常好用省去了我们自己加转义,但是使用它很容易出现转义错误的问题,所以并不建议使用它转义,而建议大家自己手动转义!
七、缓存清除函数
re.purge()函数作用就是清除正则表达式缓存,清除后释放内存。
··· END ···