其他分组
分组的形式多种多样,以上简要介绍了几种最基本的,在上述内容基础上,可以进一步探讨其他分组形式。
(?P<name><regex>)
在前面的操作中,如果有多个正则表达式分组,可以用从1开始(注意不是从0开始)的需要,获得相应分组捕获的对象。例如:
代码语言:javascript复制>>> m = re.search('(w ),(w ),(w )', 'foo,quux,baz')
>>> m.groups()
('foo', 'quux', 'baz')
>>> m.group(1, 2, 3)
('foo', 'quux', 'baz')
此外,为了意义更明确,我们也可以用(?P<name><regex>)
方式,给每个分组命名,之后通过命名得到每组捕获的对象。
>>> m = re.search('(?P<w1>w ),(?P<w2>w ),(?P<w3>w )', 'foo,quux,baz')
>>> m.groups()
('foo', 'quux', 'baz')
以上为每个分组分别命名为w1
、w2
、w3
,m.groups()
的执行结果没有变化。重点看下面操作:
>>> m.group('w1')
'foo'
>>> m.group('w3')
'baz'
>>> m.group('w1', 'w2', 'w3')
('foo', 'quux', 'baz')
这样,通过分组的名称,得到了相应分组捕获的对象。
有了分组名称的命名之后,原有序号依然有效,你可以混合使用。
代码语言:javascript复制>>> m = re.search('(?P<w1>w ),(?P<w2>w ),(?P<w3>w )', 'foo,quux,baz')
>>> m.group('w1')
'foo'
>>> m.group(1)
'foo'
>>> m.group('w1', 'w2', 'w3')
('foo', 'quux', 'baz')
>>> m.group(1, 2, 3)
('foo', 'quux', 'baz')
(?P=<name>)
(?P=<name>)
匹配向后引用的字符串,类似<n>
,但是这里给出了名称。例如:
>>> m = re.search(r'(w ),1', 'foo,foo')
>>> m
<_sre.SRE_Match object; span=(0, 7), match='foo,foo'>
>>> m.group(1)
'foo'
这是前面已经熟悉的<n>
模式,如果按照序号,可以捕获向后引用所对应的字符。
同样的操作,下面对分组命名,然后用(?P=<name>)
模式向后引用。
>>> m = re.search(r'(?P<word>w ),(?P=word)', 'foo,foo')
>>> m
<_sre.SRE_Match object; span=(0, 7), match='foo,foo'>
>>> m.group('word')
'foo'
(?P=<word>w )
匹配字符串'foo'
,并将它保存为word
这个命名的捕获,然后,逗号后面表示的向后引用(?P=word)
,再次匹配和捕获一个字符串'foo'
。
注意在向后引用的那部分分组命名的写法,不要在名称外面用尖括号包裹。
代码语言:javascript复制>>> m = re.match(r'(?P<num>d ).(?P=num)', '135.135')
>>> m
<_sre.SRE_Match object; span=(0, 7), match='135.135'>
>>> m.group('num')
'135'
(?:<regex>)
(?:<regex>)
与(<regex>)
类似,都是在<regex>
中指定匹配的正则表达式,但是(?:<regex>)
不会捕获所匹配的字符,以后也无法检索到。
>>> m = re.search('(w ),(?:w ),(w )', 'foo,quux,baz')
>>> m.groups()
('foo', 'baz')
>>> m.group(1)
'foo'
>>> m.group(2)
'baz'
在上面的示例中,中间的字符串quux
就没有被捕获,与它对应的就会前面正表达式中的(?:w )
。
根据条件匹配
根据条件匹配的方式有两种:
(?(<n>)<yes-regex>|<no-regex>)
:如果存在<n>
,则匹配<yes-regex>
,否则,它将匹配<no-regex>
。(?(<name>)<yes-regex>|<no-regex>)
:如果存在<name>
,则匹配<yes-regex>
,否则,它将匹配<no-regex>
。
例如:
代码语言:javascript复制regex = r'^(###)?foo(?(1)bar|baz)'
这个正则表达式示例,含义为:
^(###)?
表示要匹配以###
开头的字符串,如果找到,就根据###
的分组括号创建编号为1的组。否则,不存在改组。- 后面的
foo
,表示匹配字符串中的foo
。 - 最后,
(?(1)bar|baz)
,如果组1存在,就匹配bar
,否则baz
。
将上面的正则表达式用在下面的示例中。
代码语言:javascript复制>>> re.search(regex, '###foobar')
<_sre.SRE_Match object; span=(0, 9), match='###foobar'>
###foobar
是以###
开头,因此创建组1,然后匹配bar
,字符串中也有此匹配对象,最后返回匹配结果。
>>> print(re.search(regex, '###foobaz'))
None
###foobar
是以###
开头,因此创建组1,然后匹配bar
,但是,字符串中后面是baz
,没有匹配成功,最后返回None
。
>>> print(re.search(regex, 'foobar'))
None
foobar
不是###
开头,没有创建组1,根据条件,就要匹配baz
,但字符串中是bar
,所以返回None
。
>>> re.search(regex, 'foobaz')
<_sre.SRE_Match object; span=(0, 6), match='foobaz'>
foobar
不是###
开头,没有创建组1,根据条件,就要匹配baz
,字符串中后面恰好是bar
。
下面的正则表达式,与上面不同之处在于,对所创建的组进行了命名。
代码语言:javascript复制>>> 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
。
通过下面示例,进一步演示上述正则表达式的应用:
代码语言:javascript复制>>> re.search(regex, 'foo')
<_sre.SRE_Match object; span=(0, 3), match='foo'>
>>> re.search(regex, '#foo#')
<_sre.SRE_Match object; span=(0, 5), match='#foo#'>
>>> re.search(regex, '@foo@')
<_sre.SRE_Match object; span=(0, 5), match='@foo@'>
>>> print(re.search(regex, '#foo'))
None
>>> print(re.search(regex, 'foo@'))
None
>>> print(re.search(regex, '#foo@'))
None
>>> print(re.search(regex, '@foo#'))
None
比照前面的解释,理解上述操作结果。
Python中条件正则表达式有点深奥和具有挑战性的,替代它的一个方法,就是使用多个单独的re.search()
调用来实现相同的目标,这样代码就不会那么复杂了。
(未完,待续)
参考资料:https://realpython.com/regex-python/