re模块的常用方法
使用 re模块下的compile() 函数将正则表达式的字符串形式编译为一个 Pattern 对象。
通过 Pattern 提供的一系列方法可以对文本进行匹配查找,最后得到一个Match对象
最后使用 Match 对象提供的属性和方法获得信息
首先认识一下compile 函数,它的一般使用形式如下:
import re
# 将正则表达式编译成 Pattern 对象
pattern = re.compile(r'\w{3}')
print(pattern)
Pattern 对象的一些常用方法主要有:
• match 方法:从起始位置开始查找,一次匹配
• search 方法:从任何位置开始查找,一次匹配
• findall 方法:全部匹配,返回列表
• finditer 方法:全部匹配,返回迭代器
• split 方法:分割字符串,返回列表
• sub 方法:替换
match方法:
match方法是从字符串的pos下标处起开始匹配pattern,如果pattern结束时已经匹配,则返回一个Match对象;
如果匹配过程中pattern无法匹配,或者匹配未结束就已到达endpos,则返回None。该方法原型如下:
match(string[, pos[, endpos]]) 或者 re.match(pattern, string[, flags])
参数string表示字符串;pos表示下标,pos和endpos的默认值分别为0和len(string);参数flags用于编译pattern时指定匹配模式。
三个常见的匹配模式:
(1) re.I(re.IGNORECASE):忽略大小写(括号内是完整写法)
(2) re.M(re.MULTILINE):允许多行模式
(3) re.S(re.DOTALL):支持点任意匹配模式
import re
# 1. 得到pattern
pattern = re.compile('abc')
# 2. 使用公式对象匹配要校验的字符串 match 匹配,返回一个匹配对象match对象
match_obj = pattern.match('abcdef')
print(match_obj)
运行结果:
<re.match match="abc" 3),="" span="(0," object;=""> # 表示有匹配对象
import re
# 1. 得到pattern
pattern = re.compile('abc')
# 2. 使用公式对象匹配要校验的字符串 match 匹配,返回一个匹配对象match对象
match_obj = pattern.match('helloabc')
print(match_obj)
运行结果是:None
此时helloabc中明明是存在abc这个内容的,但是Match对象为什么是None呢?因为Match在匹配判断的时候都是从字符串的开头开始判断,如果开始没有匹配上就返回None了,但是如果代码改成下面这样就会返回一个Match对象
import re
# 1. 得到pattern
pattern = re.compile('abc')
# 2. 使用公式对象匹配要校验的字符串 match 匹配,返回一个匹配对象match对象
match_obj = pattern.match('helloabc',5)
# 3. 打印对象
print(match_obj)
上面���只是方便大家理解正则的检索过程,但是实际使用中我们可以直接通过re.match(pattern,字符串)进行检索。
比如(各位觉得结果会是什么呢?)
import re
r = re.match('abc', 'helabclo')
print(r)
结果是None,因为还是从头开始比较的,但是事实上我们要检索的内容很有可能在字符串的中间或者后面,不可能每次都在前面。此时我们就要使用search方法了。
search 方法:
使用search表示从任何位置开始查找,一次匹配,注意:是一次匹配,如果后面还有匹配的也不会查找了。
它的一般使用形式如下:
search(string[, pos[, endpos]])
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。
当匹配成功时,返回一个 Match 对象,如果没有匹配上,则返回 None。
import re
r = re.search('abc', 'helabcloabc')
print(r)
此时返回的结果:
<re.match match="abc" span="(3," object;="" 6),="">
Match对象有几个常用的方法:
group(): 用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0)
span(): 返回匹配字符串的起始位置
start():用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;
end():用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0
import re
r = re.search('abc', 'helabcloabc')
if r:
print(r.group())
print(r.span())
print(r.start())
print(r.end())
结果是:
abc
(3, 6)
3
6
如果我们的正则定义复杂一些使用上面的匹配规则,使用方式也是这样吗?
import re
match = re.search(r'([a-z]+) ([a-z]+)', 'hello Kitty hellobaby hello world') # 注意此时是区分大小写的
if match:
print(match.group(0))
print(match.group(1)) # 获取第一个分组的字符串
print(match.group(2)) # 获取第二个分组的字符串
print(match.groups())
结果是:
itty hellobaby
itty
hellobaby
('itty', 'hellobaby')
上面的正则表达式表示两组有多个a-z之间的任意字符组成的多个字符串,并且两组之间是有空格的。其中match.groups()表示的意思是(m.group(1), m.group(2), ...),上面的代码只有两组,因此只能match.group(2),但是如果match.group(3)则会报错。
可是问题又来了,search只要找到符合要求的字符串则不会继续查找,但是事实上后面仍然符合正则的仍然是存在的。
比如:hello world
要想所有的都获取到,考虑使用findall(),通过英语分析都能知道它是什么意思。
findall方法
findall 方法的使用形式如下:
findall(string[, pos[, endpos]])
其中,string 是待匹配的字符串,pos 和 endpos 是可选参数,指定字符串的起始和终点位置,默认值分别是 0 和 len (字符串长度)。findall 以列表形式返回 ,是全部能匹配的子串,如果没有匹配,则返回一个空列表。
比如上面的代码使用findall()看看获取的结果是什么?
import re
match_list = re.findall(r'([a-z]+) ([a-z]+)', 'hello Kitty hellobaby hello world') # 注意此时是区分大小写的
if match_list:
print(match_list)
结果:
[('itty', 'hellobaby'), ('hello', 'world')]
比如我们要求写一个正则,用于检索字符串所有两头是字母,中间全部是数字的字符串。
import re
s = 'h88ex890loK123Jldkl90gd3o'
m = re.findall('[a-z][0-9]*[a-z]', s, re.I)
print(m)
结果:
['h88e', 'x890l', 'oK', 'Jl', 'dk', 'l90g', 'd3o']
如果是判断一个字符串是否是数字开头呢?我们使用match、search、findall?
import re
# 注意是数字开头,所以我们主要是判断开头,所以我们使用match
match = re.match(r'\d+.*','123admin') # \d 在匹配规则上表示数字,+表示的是次数大于等于1,.表示任意字符,*表示长度是大于等于0
if match:
print('是数字开头的')
else:
print('不是数字开头的')
结果打印:是数字开头的,字符串换成:admin呢?
下面���代码的结果会是什么呢?
# 验证用户名 字母数字下划线 首字母不能是数字 长度必须6位以上
username = 'admin123'
m = re.match('[a-zA-Z_]\w{5,}$', username)
print(m.group())
finditer 方法
finditer 方法的行为跟 findall 的行为类似,也是搜索整个字符串,获得所有匹配的结果。但它返回一个顺序访问每一个匹配结果(Match 对象)的迭代器。大家可以将上面的代码改成finditer观察结果,此处不再展示代码。
split 方法
split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:
split(string[, maxsplit])
其中,maxsplit 用于指定最大分割次数,不指定将全部分割。跟字符串的分隔类似,但是这个更加灵活。
比如:
import re
s = 'hello Kitty hellobaby hello world hello8hello'
m = re.split(r'[\s\d]+', s) # 表示遇到空白字符\s或者数字\d,都会切割,如果有多个空格也可以切割不仅是一个空格或者数字
print(m)
此时得到的结果是:
['hello', 'Kitty', 'hellobaby', 'hello', 'world', 'hello', 'hello']
sub方法
sub 方法用于替换。它的使用形式如下:
sub(repl, string[, count])
repl 可以是字符串也可以是一个函数:
如果 repl 是字符串,则会使用 repl 去替换字符串每一个匹配的子串,并返回替换后的字符串,另外,repl 还可以使用 id 的形式来引用分组,但不能使用编号 0;
如果 repl 是函数,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
count 用于指定最多替换次数,不指定时全部替换
import re
# 比如替换敏感词汇
s = '小明喜欢苍老师'
m = re.sub(r'(苍井空|苍老师)', '***', s) # 括号里面的表示一组,可以是这一组中的任何一个。
print(m)
# 将里面的分数都替换成100分
msg = 'python=99,c=98,html=90'
m = re.sub(r'\d+', '100', msg)
print(m)
当然也可以使用函数,比如分数都加1分
import re
def add(temp):
print(temp) # 此处打印便于查看
score = temp.group() # 获取匹配的内容
score = int(score) + 1
return str(score)
m = re.sub(r'\d+', add, msg)
print(m)
当然在使用过程中还会涉及到分组、贪婪和非贪婪模式,此处就不展开了,有时间给大家专门写一篇文章。