作者:云游道士
网址 :https://www.cnblogs.com/yyds/p/6953348.html
注:本文仅代表原作者观点,此处仅为分享参考。
本节内容
- re模块介绍
- 使用re模块的步骤
- re模块简单应用示例
- 关于匹配对象的说明
- 说说正则表达式字符串前的r前缀
- re模块综合应用实例
- 参考文档
提示: 由于该站对MARKDOWN的表格支持的不是很好,所以本文中的表格均以图片的形式提供,大家如果看着比较模糊,可以放大来看或下载图片在本地查看。
正则表达式(Regluar Expressions)又称规则表达式,在代码中常简写为REs,regexes或regexp(regex patterns)。它本质上是一个小巧的、高度专用的编程语言。 通过正则表达式可以对指定的文本实现
匹配测试、内容查找、内容替换、字符串分割 等功能。正则表达式的语法不是本节要讲的内容(关于正则表达式的详细介绍请参考另一篇博文《正则表达式总结》),本节主要介绍的是Python中是如何使用re模块来完成正则表达式的相关操作的。
一、re模块介绍
Python中的re模块提供了一个正则表达式引擎接口,它允许我们将正则表达式编译成模式对象,然后通过这些模式对象执行模式匹配搜索和字符串分割、子串替换等操作。re模块为这些操作分别提供了模块级别的函数以及相关类的封装。
1. re模块提供的类
Python中的re模块中最重要的两个类:
类描述Regular Expression Objects正则表达式对象,用于执行正则表达式相关操作的实体Match Objects正则表达式匹配对象,用于存放正则表达式匹配的结果并提供用于获取相关匹配结果的方法
正则表达式对象中的方法和属性
通过re模块的compile()函数编译得到的正则表达式对象(下面用regex表示)支持如下方法:
参数说明:
- string: 要匹配或处理的字符串
- pos: 可选参数,表示从string字符串的哪个位置开始,相当于先对字符串做切片处理string[pos:]
- endpos: 可选参数,表示到string字符串的哪个位置结束(不包含该位置)
- maxsplit: regex.split()方法的可选参数,表示最大切割次数;默认值为0,表示能切割多少次就尽可能多的切割多少次
- count: regex.sub()和regex.subn()方法的可选参数,表示最大替换次数;默认为0,表示能替换多少次就尽可能多的替换多少次
- repl: sub和subn函数中的repl表示replacement,用于指定将匹配到的子串替换成什么内容,需要说明的是该参数的值可以是一个字符串,也可以是一个函数
说明: 如果指定了pos和endpos参数,就相当于在进行正则处理之前先对字符串做切片操作 string[pos, endpos],如rx.search(string, 5, 50)就等价于rx.search(string[5:50]),也等价于rx.search(string[:50], 5);如果endpos比pos的值小,则不会找到任何匹配。
匹配对象中的方法和属性
调用正则表达式对象的regex.match()、regex.fullmatch()和regex.search()得到的结果就是一个匹配对象,匹配对象支持以下方法和属性:
参数说明:
- template: m.expand()方法中的template参数是一个模板字符串,这个字符串中可以使用分组对应的的数值索引进行后向引用(如:1,2)或命名后向引用(如g<1>,g)来表示某个分组的占位符;m.expand()方法的执行过程实际上就是通过sub()方法把template字符串中的这些分组占位符用当前匹配对象中的数据进行替换。
- default: m.groups()与m.groupdict()方法中的default都是为未匹配成功的捕获组提供默认匹配值的。
- group: m.group()、m.start()、m.end()和m.span()方法中的group参数都表示要选择的分组索引值,1表示第一个分组,2表示第二个分组,依次类推,group参数的默认值是0,表示整个正则表达式所匹配的内容。
2. re模块提供的函数
re模块提供了以下几个模块级别的函数
将pattern参数只能是字符串;
- string: 需要用正则表达式来匹配的字符串对象
- flags: 一个标志位,它会影响正则表达式对象的匹配行为,可取值下面会介绍;但是有一点需要说明的是,只有当pattern参数是字符串时才能指定这个flags参数,否则会报错;如果pattren参数是一个正则表达式对象,则flags参数需要在调用re.compile()函数时指定。
- repl: sub和subn函数中的repl表示replacement,用于指定将匹配到的子串替换成什么内容,需要说明的是该参数的值可以是一个字符串,也可以是一个函数
- count: sub和subn函数中的count表示最多可替换次数
- maxsplit: split函数中的maxsplit蚕食表示最大分隔次数
说明: 通过对比会发现,上面这些re模块级别的函数除了re.compile、re.purge和re.escape这几个函数外,其它函数名都与正则表达式对象支持的方法同名。实际上re模块的这些函数都是对正则表达式对象相应方法的封装而已,功能是相同的。只是少了对pos和endpos参数的支持,但是我们可以手动通过字符串切片的方式来达到相应的需求。个人认为,我们应该尽可能的使用模块级别的函数,这样可以增强代码的兼容性。
3. 标志位flags
由上面的描述可知,flags参数在上面这些模块函数中是要个可选参数,re模块中预定了该参数可取的值:
说明: 这些flag可以单独使用,也可以通过逻辑或操作符'|'进行拼接来联合使用。
二、使用re模块的步骤
我们有必要对re模块中所包含的类及其工作流程进行一下简单的、整体性的说明,这讲有利于我们对下面内容的理解。
1. 使用re模块进行正则匹配操作的步骤
- 1)编写表示正则表达式规则的Python字符串str;
- 2)通过re.compile()函数编译该Python字符串获得一个正则表达式对象(Pattern Object)p;
- 3)通过正则表达式对象的p.match()或p.fullmatch()函数获取匹配结果--匹配对象(Match Object)m;
- 4)通过判断匹配对象m是否为空可知是否匹配成功,也可以通过匹配对象m提供的方法获取匹配内容。
2. 使用re模块进行内容查找、替换和字符串分隔操作的步骤
- 1)编写表示正则表达式规则的Python字符串str;
- 2)通过re.compile()函数编译该Python字符串获得一个正则表达式对象(Pattern Object)p;
- 3)通过正则表达式对象的p.search()或p.findall()或p.finditer()或p.sub()或p.subn()或p.split()函数完内容查找、替换和字符串分隔操作并获取相应的操作结果;
总结: 关于正则表达式的语法和编写示例请参考《正则表达式总结》)。根据上面的描述可知,将一个表示正则表达式的Python字符串编译成一个正则表达式对象是使用正则表达式完成相应功能的首要步骤,re模块中用于完成正则表达式编译功能的函数为re.compile()。
三、re模块简单应用示例
在上面的内容中,我们已经列出了re模块所提供的类,以及这些类的对象所支持的函数和属性,还有re模块所提供的模块级别的函数。这里我们要讨论的是怎样合理的使用这些方法和函数,换句话说就是在什么情况下使用这些方法和函数,以及如何使用的问题。我们之前说过,正则表达式主要可以用来提供以下几个功能:
- 匹配测试
- 子串/内容查找
- 子串/内容替换
- 字符串分割
下面我们就分别通过对这几个功能的实例实现对上面这些函数和方法以及参数和属性的使用做下说明和具体的解释。
1. 匹配测试
匹配测试,意思是通过特定的正则表达式对一个指定的字符串进行匹配来判断该字符串是否符合这个正则表达式所要求的格式。常见的使用场景举例:
- 查看某个字符串(通常来自用户输入)是否是一个邮箱或电话号码
- 用户注册时填写的用户名和密码是否符合指定需求
使用的函数或方法
通过re模块执行匹配测试时可以使用的函数或方法是:
- re模块级别的match()和fullmatch()函数
- 正则表达式对象的match()和fullmatch()方法
实例1
前面提到过,match()函数或方法只是匹配字符串的开始位置,而fullmatch匹配的是整个字符串;fullmatch()函数或方法就相当于给match()函数或方法的pattern或string参数加上行首边界元字符'^'和行尾边界元字符'$',下面来看个例子:
import re# 定义一个函数来对匹配结果进行展示 def display_match_obj(match_obj): if match_obj is None: print('Regex Match Fail!') else: print('Regex Match Success!', match_obj)if __name__ == '__main__': p = re.compile(r'[a-z]+') display_match_obj(p.match('hello')) display_match_obj(p.match('hello123')) display_match_obj(p.fullmatch('hello')) display_match_obj(p.fullmatch('hello123')) display_match_obj(p.match('123hello')) display_match_obj(p.match('123hello', 3))
输出结果:
Regex Match Success! <_sre.sre_match object span="(0," match="hello">Regex Match Success! <_sre.sre_match object span="(0," match="hello">Regex Match Success! <_sre.sre_match object span="(0," match="hello">Regex Match Fail!Regex Match Fail!Regex Match Success! <_sre.sre_match object span="(3," match="hello">
分析:
- '[a-z]+'能与'hello'和'hello123'的开头部分匹配
- '[a-z]+'能与'hello'完全匹配
- '[a-z]+'不能与'hello123'完全匹配
- '[a-z]+'不能与'123hello'的开头部分匹配
- '[a-z]+'能与'123hello'的切片'123hello'[3:]的开头部分匹配
实例2
上面使用的是正则表达式对象的match()和fullmatch()方法,我们也可以通过re提供的模块级别的函数来实现:
if __name__ == '__main__': p = re.compile(r'[a-z]+') display_match_obj(re.match(p, 'hello')) display_match_obj(re.match(p, 'hello123')) display_match_obj(re.fullmatch(p, 'hello')) display_match_obj(re.fullmatch(p, 'hello123')) display_match_obj(re.match(p, '123hello')) display_match_obj(re.match(p, '123hello'[3:])) # 唯一不同的是这里
输出结果跟上面是一样的
re模块的match()和fullmatch()函数不支持pos和endpos参数,所以只能通过字符串切片先对字符串进行切割。
实例3
再来看个对比:
if __name__ == '__main__': display_match_obj(re.match(r'[a-z]+', 'hello123')) display_match_obj(re.match(r'[a-z]+$', 'hello123')) display_match_obj(re.match(r'^[a-z]+$', 'hello123'))
输出结果:
Regex Match Success! <_sre.sre_match object span="(0," match="hello">Regex Match Fail!Regex Match Fail!
分析:
- re.match()和re.fullmatch()中的pattern参数可以是正则表达式对象,也可以是Python字符串
- re.match()函数中的正则表达式参数加上边界元字符'^'和'$'就相当于re.fullmatch()了
- 当匹配过程是从字符串的第一个字符开始匹配时re.match(r'^[a-z]+$', 'hello123') 与 re.match(r'[a-z]+$', 'hello123')效果是一样的,因为re.match()本来就是从字符串开头开始匹配的;但是,如果匹配过程不是从字符串的第一个字符开始匹配时,它们是有区别的,具体请看下面这个例子。
实例4
if __name__ == '__main__': p1 = re.compile(r'[a-z]+$') p2 = re.compile(r'^[a-z]+$') display_match_obj(p1.match('123hello', 3)) display_match_obj(p2.match('123hello', 3))
输出结果:
Regex Match Success! <_sre.sre_match object span="(3," match="hello">Regex Match Fail!
这个很好理解,因为元字符'^'匹配的是表示字符串开始位置的特殊字符,而不是字符串内容的第一个字符。match()匹配的是字符串内容的第一个字符,因此即使是在MULTILINE模式,re.match()也将只匹配字符串的开始位置,而不是该字符串每一行的行首。
2. 内容查找
内容查找,意思是通过特定的正则表达式对一个指定的字符串的内容进行扫描来判断该字符串中是否包含与这个正则表达式相匹配的内容。
使用的函数或方法
通过re模块执行匹配测试时可以使用的函数或方法是:
- re模块级别的search()、findall()和 finditer()函数
- 正则表达式对象的search()、findall()和finditer()方法
实例1:search() vs match()
这个例子中,我们来看下search()函数的使用,以及它与match()函数的对比。
Python提供了两个不同的基于正则表达式的简单操作:
- re.match(): 该函数仅是在字符串的开始位置进行匹配检测
- re.search(): 该函数会在字符串的任意位置进行匹配检测
import restring = 'abcdef'print(re.match(r'c', string))print(re.search(r'c', string))
输出结果:
None<_sre.sre_match object span="(2," match="c">
这个比较简单,不做过多解析。
我们应该能够想到,当正则表达式以'^'开头时,search()也会从字符串的开头进行匹配:
import restring = 'abcdef'print(re.match(r'c', string))print(re.search(r'^c', string))print(re.search(r'^a', string))
输出结果:
NoneNone<_sre.sre_match object span="(0," match="a">
这里再重复一下上面提到过的内容,就是元字符'^'与match()的从字符串开始位置开始匹配并不是完全等价的。因为元字符'^'匹配的是表示字符串开始位置的特殊字符,而不是字符串内容的第一个字符。match()匹配的是字符串内容的第一个字符,因此即使是在MULTILINE模式,re.match()也将只匹配字符串的开始位置,而不是该字符串每一行的行首;相关search('^...')却可以匹配每一行的行首。
下面来看个例子:
string = '''ABX'''print(re.match(r'X', string, re.MULTILINE))print(re.search(r'^X', string, re.MULTILINE))
输出结果:
None<_sre.sre_match object span="(5," match="X">
实例2:findall()与finditer()
findall()与finditer()也是用来查找一个字符串中与正则表达式相匹配的内容,但是从名字上就能看出来,findall()与finditer()会讲这个字符串中所有与正则表达式匹配的内容都找出来,而search()仅仅是找到第一个匹配的内容。另外findall()返回的是所有匹配到的子串所组成的列表,而finditer()返回的是一个迭代器对象,该迭代器对象会将每一次匹配到的结果都作为一个匹配对象返回。下面来看一个例子:
尝试找出一个字符串中的所有副词(英语的副词通常都是以字母‘ly’结尾):
import retext = "He was carefully disguised but captured quickly by police."print(re.findall(r"w+ly