Python之正则表达式模块(re)详解

1,950 阅读10分钟
原文链接: mp.weixin.qq.com

本文章以授权于微信公众号:CodeId有什么建议可以到公众号里进行留言。

在上一篇文章Python之正则表达式基础语法中我们简单的学习了正则表达式的一些基础语法,今天这篇文章将讲一下python如何使用正则表达式,它有那些方法。

正则表达式模块(re)内容

re模块是python的一个库,里面有很多方法可以对正则表达式进行运用。

re.search(pattern,string,flags = 0)

参数 描述
pattern 正则表达式
string 要匹配的字符串
flags 标志值,用于改变正则表达式的行为

flags有哪些值:

标志 含义
re.S(DOTALL) 使.匹配包括换行在内的所有字符
re.I(IGNORECASE) 使匹配对大小写不敏感
re.L(LOCALE) 做本地化识别(locale-aware)匹配,法语等
re.M(MULTILINE) 多行匹配,影响^$
re.X(VERBOSE) 该标志通过给予更灵活的格式以便将正则表达式写得更易于理解
re.U(UNICODE) 根据Unicode字符集解析字符。在python3是多余的, python3默认使用Unicode字符串
re.A(ASCII) 使\w, \W,\b, \B,\d, \D,\s\S只执行ASCII匹配而不完全是 Unicode匹配。

注:下面方法中的同名参数和上述介绍的参数意思一样,将不再过多的解释。

作用:从左到右扫描整个字符串 返回第一个与正则表达式匹配的对象,如果没有匹配成功就返回None

比如: 查找 字符串中 CodeId 第一次出现的位置

1import re2text = 'Welcome to CodeId.  CodeId'3result = re.search(r'CodeId',text)4print(result.start())5# 结果为:11

注:返回的result为匹配对象,其中 result.start()是返回开始匹配的位置索引。由此我们可以猜到result.end()应该是返回匹配结束时的位置索引。

re.match(pattern,string,flags = 0 )

作用:查找字符串开头的零个或几个字符是否与正则表达式匹配,如果成功则返回相应的匹配对象,否则返回None

比如:判断 python变量 的开头是否是数字。

1result = re.match(r'\d+','123CodeId')2if result :3    print("python变量 的开头含有数字")4# 结果为:python变量 的开头含有数字

注意:re.match()只会匹配字符串的开头,即使在 MULTILINE(多行)模式下,也不会匹配每行的开头。

1result = re.match(r'a','b\na',re.MULTILINE)2print(result)3# 结果为:None4# 证明匹配失败

re.fullmatch(pattern,string,flags = 0 )

作用:如果整个字符串都与正则表达式匹配,则返回相应的匹配对象,否则返回None

比如:判断输入的邮箱是否合法

1# 判断输入的邮箱是否合法2email = '12323123@qq.com'3result = re.fullmatch(r'^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$',email)4if result :5    print("你输入的邮箱:",result.group(),"是合法的")6# 结果为:你输入的邮箱: 12323123@qq.com 是合法的

注:result.group()是匹配对象的一个方法,作用是:返回匹配的所有子串。后面会进行详细介绍。

re.split(pattern,string,maxsplit = 0,flags = 0 )

作用:以字符串中与正则表达式匹配的子串,为分隔符,对字符串进行分割。返回分割之后的列表。比如:把英文句子分割为单词组成的列表

1text = 'Welcome to CodeId'2result = re.split(r'\W+',text)3print(result)4# 结果为:['Welcome', 'to', 'CodeId']

参数maxsplit为最大分割次数,默认是 0表示无限分割。

1text = 'Welcome to CodeId'2result = re.split(r'\W+',text,maxsplit=1)3print(result)4# 结果为:['Welcome', 'to CodeId']5# 最大分割一次 剩下的部分 作为列表的最后一个元素

注意:如果在正则表达式中使用组括号,则正则表达式中所有组的文本也会作为结果列表的一部分返回。

1text = 'Welcome to CodeId'2result = re.split(r'(\W+)',text)3print(result)4# 结果为:['Welcome', ' ', 'to', ' ', 'CodeId']

re.findall(pattern,string,flags = 0 )

作用:从左到右对字符串进行扫描,以列表的形式返回所有与正则表达式匹配的子串。比如:匹配一句话中所有的带小数点的数

1text = '我有3.5元钱,给你1.5元,我还剩2元'2result = re.findall(r'-?\d+\.\d+',text)3print(result)4# 结果为 ['3.5', '1.5']

注意:如果正则表达式中存在一个组,则返回组列表; 如果正则表达式中有多个组,这将是一个元组列表。

1text = '我有3.5元钱,给你1.5元,我还剩2元'2result = re.findall(r'(-?\d+\.\d+)',text) # 一个组3print(result)4# 结果为 ['3.5', '1.5']5result = re.findall(r'(-?\d)+(\.\d+)',text) # 多个组6print(result)7# 结果为 [('3', '.5'), ('1', '.5')]

re.finditer(pattern,string,flags = 0)

作用:从左到右对字符串进行扫描,以迭代器的形式返回所有与正则表达式匹配的子串。比如:上面的例子 ,换成以迭代器的形式返回

 1# 匹配一句话中所有的带小数点的数 2text = '我有3.5元钱,给你1.5元,我还剩2元' 3result = re.finditer(r'-?\d+\.\d+',text) # 返回一个迭代器 4print(result) 5# 结果为 <callable_iterator object at 0x0000025D2B81B320> 6for s in result: 7    # 每个元素都是 匹配对象 8    print(s.group()) 9# 结果为:10'''113.5121.513'''

re.sub(pattern,repl,string,count = 0,flags = 0)

作用:把字符串中与正则表达式匹配的子串替换为replrepl可以是一个字符串也可以是一个函数。

  • repl是一个字符串时,可以处理任何转义字符比如:

  1. 处理普通字符串

1# 把关键信息替换为 ****,比如手机号2text = '有事请拨打电话:15589878888'3result = re.sub(r'(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}','****',text)4print(result)5# 结果为:有事请拨打电话:****
  1. 反向引用

1#隐藏手机号的中间四位数2text = '有事请拨打电话:15589878888'3# 字符串 r'\1****\3' 中的 \1和\3 就是应用组1和组3的内容4result = re.sub(r'(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])(\d{4})(\d{4})',r'\1****\3',text)5print(result)6# 结果为:有事请拨打电话:155****8888

注:除了上述的字符转义和反向引用外, 使用\g<name>还可以引用那些使用 (?P<name>...)语法定义的组。同时\g<number>等价于 \number,\g<number>在替代方法中表达的更加清楚。后面会进行举例。反向引用 \g<0>替换正则表达式中匹配的整个子串。

  • repl是一个函数时,这个函数的参数是一个单匹配对象,并返回一个替换的字符串。比如:

 1def dashrepl(matchobj): 2    if matchobj.group(0) == '-': 3        return ' ' 4    else: 5        return '*' 6result = re.sub(r'-{1,3}', dashrepl, 'pro--a--gram-files') 7print(result) 8# 结果为:pro*a*gram files 910text = "JGood is a handsome boy, he is cool, clever, and so on..."11print(re.sub(r'\S+', lambda m:'['+m.group(0)+']', text,0))12# 结果为:[JGood] [is] [a] [handsome] [boy,] [he] [is] [cool,] [clever,] [and] [so] [on...]
  • 参数count用于控制最大替换次数。 count必须是一个非负整数。如果省略或为零,则所有匹配的都将被替换。比如:

1result = re.sub(r'-',r'*','------------------',count=4)2print(result)3# 结果为:****--------------4result = re.sub(r'-',r'*','------------------',count=8)5print(result)6# 结果为:********----------

re.subn(pattern,repl,string,count = 0,flags = 0)

作用:和re.sub()一样,只是 re.subn()返回的是一个元组。元组里面的内容包括替换后的字符串,和替换的次数比如:

1# 把一些私密信息替换为 ****,比如手机号2text = '有事请拨打电话:15589878888,以前的手机号:15888886666 不在使用'3result = re.subn(r'(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}','****',text)4print(result)5# 结果为:('有事请拨打电话:****,以前的手机号:**** 不在使用', 2)

re.escape(pattern)

作用:对字符串中的特殊字符加上转义,也就是在每个特殊字符前面加上\

比如:

1result = re.escape('CodeId*&@_.\.')2print(result)3# 结果为 CodeId\*\&\@_\.\\\.

re.purge()

作用:清除正则表达式缓存。

re.compile(pattern,flags = 0)

作用:把一个正则表达式编译为正则表达式对象,它可用于匹配match()search()以及其他方法。比如:

1com = re.compile('CodeId')2result = com.search('Welcome to CodeId.CodeId')3print(result.start())4# 结果是 11

注:正则表达式对象的匹配方法和上面介绍的模块级匹配方法 在得到效果上是一样的,但是效率上有所不同,re.compile()是对正则表达式进行一次编译,然后保存便以后重用,适合那些单个程序中多次使用的正则表达式。正则表达式对象的方法在参数上和上面的方法有些不同。比如: Pattern.match(string [,pos [,endpos ] ])

  • 参数pos用于设置字符串在什么位置开始匹配。

  • 参数endpos用于设置字符串最长的匹配距离。其他的所有方法和上面差不多 不在细讲,可以查看https://docs.python.org/3/library/re.html了解更多详细的内容。

匹配对象(Match)

匹配对象的布尔值始终返回True。 由于match()search()在没有匹配时返回None,因此我们可以通过 if语句测试是否是匹配对象。比如:

1result = re.search(r"CodeId", "Welcome to CodeId")2if result :3    print('Yes')4# 结果为 Yes

Match.expand(template)

作用:把模板字符串template中指定的位置 替换为 指定组的内容。template中可以使用 \id\g<id>\g<name>引用分组,但不能使用编号0\id\g<id>是等价的;如果你想表达 \1之后是字符'0',只能使用 \g<1>0,因为\10将被认为是第 10个分组。

比如:匹配日期

 1# 匹配日期 2data = '2018-8-9' 3result = re.fullmatch(r'^(\d{4})-(\d{1,2})-(?P<day>\d{1,2})$',data) 4 5expand = result.expand(r'今天是 \1 年 \g<2> 月 \g<day> 号') 6print(expand) 7# 结果为 今天是 2018 年 8 月 9 号 8 9print(result.group())10# 结果为 2018-8-9

Match.group([ group1,… ] )

作用:返回匹配的一个或多个子组。其中如果有一个参数,结果是返回一个单一的字符串;如果有多个参数,则结果是返回每个参数对应的组内容 组成的 元组。没有参数,group1默认为 0(整个匹配结果被返回)。如果groupN参数为零,则相应的返回值是整个匹配的字符串比如:

 1data = '2018-8-9' 2result = re.fullmatch(r'^(\d{4})-(\d{1,2})-(?P<day>\d{1,2})$',data) 3print(result.group()) # 没有参数时 4# 结果为 2018-8-9 5print(result.group(1)) # 一个参数时 6# 结果为 2018 7print(result.group(1,2)) # 两个参数时 8# 结果为 ('2018', '8') 9print(result.group(1,2,3,0)) # 参数中有为 0 的10# 结果为 ('2018', '8', '9', '2018-8-9')

如果组编号为负数或大于正则表达式中定义的组数,则会引发IndexError异常。

1print(result.group(4)) # 超过组数2# 结果为 IndexError: no such group

如果正则表达式使用(?P<name>...)语法,则 groupN参数也可以是通过其组名称来访问组内容。

1print(result.group('day')) # 通过组名称2# 结果为 9 

如果一个组匹配多次,只能访问最后一次匹配。注:返回结果还可以通过数组的方式来访问,这得益于 Match.__getitem__(g)方法,它是在 3.6版本中新加的。

1data = '2018-8-9'2result = re.fullmatch(r'^(\d{4})-(\d{1,2})-(?P<day>\d{1,2})$',data)3print(result[0])4# 结果为 2018-8-95print(result[1])6# 结果为 20187print(result['day'])8# 结果为 9

Match.groups(default=None)

作用:从1开始,返回一个包含匹配所有子组的元组。参数 default为那些没有匹配成功的子组设置默认值。没有参数时默认是None.比如:匹配浮点数

1result = re.match(r"(\d+)\.?(\d+)?", "24")2print(result.groups())3# 结果为 ('24', None)4print(result.groups('0')) # 设置默认参数为 05# 结果为 ('24', '0')

Match.groupdict(default=None)

作用:对于使用(?P<name>...)语法的所有子组,以字典的形式返回。默认参数 default用于那些没有匹配成功的子组; 它默认为None

1result = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")2print(result.groupdict())3# 结果为 {'first_name': 'Malcolm', 'last_name': 'Reynolds'}

Match.start([group])和Match.end([group])

作用:返回按组匹配子串的开始和结束索引; 组默认为零(意味着整个匹配的子字符串)。如果组没有匹配成功则返回 -1。比如:

1result = re.match(r"(\d+)\.?(\d+)?", "24.59")2print(result.start()) # 整个串的开始索引3# 结果为 04print(result.end()) # 整个串的结束索引5# 结果为 56print(result.start(2)) # 第二组的开始索引7# 结果为 38print(result.end(2)) # 第二组的结束索引9# 结果为 5

Match.span([group])

作用:返回一个由匹配子组的开始和结束组成的元组。如果分组没有成功匹配,返回(-1,-1)。比如:

1result = re.match(r"(\d+)\.?(\d+)?", "24.59")2print(result.span())3# 结果为 (0, 5)4print(result.span(2))5# 结果为 (3, 5)

下面是公众号:CodeId 欢迎关注

公众号:CodeId