Python中的正则表达式(五)
2020-05-21
Python中的正则表达式(五)
作者:老齐
与本文相关的图书推荐:《跟老齐学Python:轻松入门》

《Python正则表达式》这个系列,已经完成了四篇,本文是第五篇,请继续阅读。
如果错过了前几篇,请关注微信公众号:老齐教室。
其他分组
分组的形式多种多样,以上简要介绍了几种最基本的,在上述内容基础上,可以进一步探讨其他分组形式。
(?P<name><regex>)
在前面的操作中,如果有多个正则表达式分组,可以用从1开始(注意不是从0开始)的需要,获得相应分组捕获的对象。例如:
1 | >>> m = re.search('(\w+),(\w+),(\w+)', 'foo,quux,baz') |
此外,为了意义更明确,我们也可以用(?P<name><regex>)方式,给每个分组命名,之后通过命名得到每组捕获的对象。
1 | >>> m = re.search('(?P<w1>\w+),(?P<w2>\w+),(?P<w3>\w+)', 'foo,quux,baz') |
以上为每个分组分别命名为w1、w2、w3,m.groups()的执行结果没有变化。重点看下面操作:
1 | >>> m.group('w1') |
这样,通过分组的名称,得到了相应分组捕获的对象。
有了分组名称的命名之后,原有序号依然有效,你可以混合使用。
1 | >>> m = re.search('(?P<w1>\w+),(?P<w2>\w+),(?P<w3>\w+)', 'foo,quux,baz') |
(?P=<name>)
(?P=<name>)匹配向后引用的字符串,类似\<n>,但是这里给出了名称。例如:
1 | >>> m = re.search(r'(\w+),\1', 'foo,foo') |
这是前面已经熟悉的\<n>模式,如果按照序号,可以捕获向后引用所对应的字符。
同样的操作,下面对分组命名,然后用(?P=<name>)模式向后引用。
1 | >>> m = re.search(r'(?P<word>\w+),(?P=word)', 'foo,foo') |
(?P=<word>\w+)匹配字符串'foo',并将它保存为word这个命名的捕获,然后,逗号后面表示的向后引用(?P=word),再次匹配和捕获一个字符串'foo'。
注意在向后引用的那部分分组命名的写法,不要在名称外面用尖括号包裹。
1 | >>> m = re.match(r'(?P<num>\d+)\.(?P=num)', '135.135') |
(?:<regex>)
(?:<regex>)与(<regex>)类似,都是在<regex>中指定匹配的正则表达式,但是(?:<regex>)不会捕获所匹配的字符,以后也无法检索到。
1 | >>> m = re.search('(\w+),(?:\w+),(\w+)', 'foo,quux,baz') |
在上面的示例中,中间的字符串quux就没有被捕获,与它对应的就会前面正表达式中的(?:\w+)。
根据条件匹配
根据条件匹配的方式有两种:
(?(<n>)<yes-regex>|<no-regex>):如果存在<n>,则匹配<yes-regex>,否则,它将匹配<no-regex>。(?(<name>)<yes-regex>|<no-regex>):如果存在<name>,则匹配<yes-regex>,否则,它将匹配<no-regex>。
例如:
1 | regex = r'^(###)?foo(?(1)bar|baz)' |
这个正则表达式示例,含义为:
^(###)?表示要匹配以###开头的字符串,如果找到,就根据###的分组括号创建编号为1的组。否则,不存在改组。- 后面的
foo,表示匹配字符串中的foo。 - 最后,
(?(1)bar|baz),如果组1存在,就匹配bar,否则baz。
将上面的正则表达式用在下面的示例中。
1 | >>> re.search(regex, '###foobar') |
###foobar是以###开头,因此创建组1,然后匹配bar,字符串中也有此匹配对象,最后返回匹配结果。
1 | >>> print(re.search(regex, '###foobaz')) |
###foobar是以###开头,因此创建组1,然后匹配bar,但是,字符串中后面是baz,没有匹配成功,最后返回None。
1 | >>> print(re.search(regex, 'foobar')) |
foobar不是###开头,没有创建组1,根据条件,就要匹配baz,但字符串中是bar,所以返回None。
1 | >>> re.search(regex, 'foobaz') |
foobar不是###开头,没有创建组1,根据条件,就要匹配baz,字符串中后面恰好是bar。
下面的正则表达式,与上面不同之处在于,对所创建的组进行了命名。
1 | >>> regex = r'^(?P<ch>\W)?foo(?(ch)(?P=ch)|)$' |
将这个正则表达式分解,并说明其含义:
^:字符串的开始(?P<ch>\W):匹配一个非字母字符,并将改组捕获对象命名为ch。(?P<ch>\W)?:以上情况,匹配0个或1个。foo:匹配字符串foo。(?(ch)(?P=ch)|):如果ch的组存在,匹配的内容和ch组一样,否则为空。$:字符串的结尾
如果非字母字符位于foo之前,则解析器创建一个名为ch的组,其中包含该字符。然后,条件匹配匹配<yes-regex>,它是(?P=ch),还是同样的字符。这意味着相同的字符也必须跟在foo后面,这样整个匹配才会成功。
如果foo前面没有非字母字符,那么解析器就不会创建ch组,<no-regex>是空字符串,这意味着在foo后面必须没有任何内容,整个匹配才会成功。因为^和$锚定整个正则表达式,所以字符串必须恰好等于foo。
通过下面示例,进一步演示上述正则表达式的应用:
1 | >>> re.search(regex, 'foo') |
比照前面的解释,理解上述操作结果。
Python中条件正则表达式有点深奥和具有挑战性的,替代它的一个方法,就是使用多个单独的re.search()调用来实现相同的目标,这样代码就不会那么复杂了。
(未完,待续)
参考资料:https://realpython.com/regex-python/
搜索技术问答的公众号:老齐教室
在公众号中回复:老齐,可查看所有文章、书籍、课程。
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
关注微信公众号,读文章、听课程,提升技能