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/
搜索技术问答的公众号:老齐教室
在公众号中回复:老齐,可查看所有文章、书籍、课程。
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
关注微信公众号,读文章、听课程,提升技能