注意:正则中常用的前项界定 (?<=exp) 和前项否定界定 (?<!exp) 在 Python 中可能会报错:look-behind requires fixed-width pattern,原因是 python 中 前项界定的表达式必须是定长的,看如下示例:
(?<=aaa) # 正确(?<=aaa|bbb) # 正确
(?<=aaa|bb) # 错误
(?<=\d+) # 错误
(?<=\d{3}) # 正确
2.3、条件匹配
这大概是最复杂的正则表达式了。语法如下:
语法 描述(?(id/name)yes|no) 如果指定分组存在,则匹配 yes 模式,否则匹配 no 模式
此语法极少用到,印象中只用过一次。
以下示例的要求是:如果以 _ 开头,则以字母结尾,否则以数字结尾。
s1 = '_abcd's2 = 'abc1'
pattern = '(_)?[a-zA-Z]+(?(1)[a-zA-Z]|\d)'
re.search(pattern, s1).group()
re.search(pattern, s2).group()
结果:
_abcdabc1
2.4、findall
Python 中的 re.findall 是个比较特别的方法(之所以说它特别,是跟我常用的 C# 做比较,在没看注释之前我想当然的掉坑里去了)。我们看这个方法的官方注释:
Return a list of all non-overlapping matches in the string.If one or more capturing groups are present in the pattern, return
a list of groups; this will be a list of tuples if the pattern
has more than one group.
Empty matches are included in the result.
简单来说,就是
如果没有分组,则返回整条正则匹配结果的列表;
如果有 1 个分组,则返回分组匹配到的结果的列表;
如果有多个分组,则返回分组匹配到的结果的元组的列表。
看下面的例子:
s = 'aaa123bbb456ccc're.findall('[a-z]+\d+', s) # 不包含分组
re.findall('[a-z]+(\d+)', s) # 包含一个分组
re.findall('([a-z]+(\d+))', s) # 包含多个分组
re.findall('(?:[a-z]+(\d+))', s) # ?: 不捕获分组匹配结果
结果:
['aaa123', 'bbb456']['123', '456']
[('aaa123', '123'), ('bbb456', '456')]
['123', '456']
零宽断言中讲到 Python 中前项界定必须是定长的,这很不方便,但是配合 findall 有分组时只取分组结果的特性,就可以模拟出非定长前项界定的效果了。
结语其实正则就像是一个数学公式,会背公式不一定会做题。但其实这公式一点也不难,至少比学校里学的数学简单多了,多练习几次也就会了。