《Python正则表达式》这个系列,已经完成了三篇,本文是第四篇,请继续阅读。
如果错过了前两篇,请关注微信公众号:老齐教室。
前面已经对Python中正则表达的基本内容做了比较完整的讲述,从本文开始,将进入高级应用部分。
分组,就是将一个正则表达式分成若干个子表达式。分组包括两个操作:
通常,用( )
表示一个分组,在其中写子表达式。
>>> re.search('(bar)', 'foo bar baz')
<_sre.SRE_Match object; span=(4, 7), match='bar'>
>>> re.search('bar', 'foo bar baz')
<_sre.SRE_Match object; span=(4, 7), match='bar'>
在这个示例中,(bar)
是一个分组的正则表达式,即一个子表达式,但是,这里因为只有一组,所以与不分组的bar
效果是一样。
如果在一个分组的子表达式后面跟一个量词,那么就是把这个分组作为一个单元。
例如,(bar)+
,意味着将字符串bar
看做一个单元,即要匹配至少1个bar
。
>>> re.search('(bar)+', 'foo bar baz')
<_sre.SRE_Match object; span=(4, 7), match='bar'>
>>> re.search('(bar)+', 'foo barbar baz')
<_sre.SRE_Match object; span=(4, 10), match='barbar'>
>>> re.search('(bar)+', 'foo barbarbarbar baz')
<_sre.SRE_Match object; span=(4, 16), match='barbarbarbar'>
下面用表格的方式,比较bar+
和(bar)+
的区别:
正则表达式 | 说明 | 举例 |
---|---|---|
bar+ | +作用在字符r,匹配的字符串要求在ba的后面可以有1个或更多个r | 'bar', 'barr', 'barrr' |
(bar)+ | +作用在bar,匹配的字符串中药出现bar | 'bar', 'barbar', 'barbarbar' |
下面再看个复杂的示例。
>>> re.search('(ba[rz]){2,4}(qux)?', 'bazbarbazqux')
<_sre.SRE_Match object; span=(0, 12), match='bazbarbazqux'>
>>> re.search('(ba[rz]){2,4}(qux)?', 'barbar')
<_sre.SRE_Match object; span=(0, 6), match='barbar'>
正则表达式(ba[rz]){2,4}(qux)?
中,(ba[rz])
是一个分组,表示要匹配bar
或baz
。(ba[rz]){2,4}
则表示匹配的数量范围(2到4个),后面的(qux)?
又将qux
作为一个单元,匹配0个或1个。
在上述示例基础上,进一步理解:
>>> re.search('(foo(bar)?)+(\d\d\d)?', 'foofoobar')
<_sre.SRE_Match object; span=(0, 9), match='foofoobar'>
>>> re.search('(foo(bar)?)+(\d\d\d)?', 'foofoobar123')
<_sre.SRE_Match object; span=(0, 12), match='foofoobar123'>
>>> re.search('(foo(bar)?)+(\d\d\d)?', 'foofoo123')
<_sre.SRE_Match object; span=(0, 9), match='foofoo123'>
下面用表格的方式,将上面示例中的几个表达式进行说明。
匹配foo后面跟着0个或1个bar
将上表中的组合在一起,就是(foo(bar)?)+(\d\d\d)?
正则表达式。
分组的目的之一是搜索字符串,根据分组原则捕获相应部分。
在re
模块中,有re.search()
,可以返回搜索到的匹配对象,针对分组操作,此对象有.groups
和.group
两个方法。
m.groups()
m.groups()
根据正则表达式,返回所有分组所捕获的字符串。
>>> m = re.search('(\w+),(\w+),(\w+)', 'foo,quux,baz')
>>> m
<_sre.SRE_Match object; span=(0, 12), match='foo:quux:baz'>
在此示例中,正则表达式(\w+),(\w+),(\w+)
包含三个组,每组都是要匹配至少1个字母、数字,即要从foo,quux,baz
中得到三个字符串foo
、quux
、baz
,不包含原字符串中的,
。
>>> m.groups()
('foo', 'quux', 'baz')
执行m.groups()
,返回结果为一个元组,其中包括所捕获的三个字符串。
m.group(n)
m.groups()
返回的元组,包含了所有捕获的内容。但在操作中,可能需要返回某个分组,此时使用m.group(n)
方法实现。
>>> m = re.search('(\w+),(\w+),(\w+)', 'foo,quux,baz')
>>> m.groups()
('foo', 'quux', 'baz')
>>> m.group(1)
'foo'
>>> m.group(2)
'quux'
>>> m.group(3)
'baz'
m.group(1)
中的参数是1
,表示捕获第一个分组的结果——注意,不是从0开始。如果参数为0
,会是下面的结果:
>>> m.group(0)
'foo,quux,baz'
>>> m.group() # 不写参数,即默认参数是0
'foo,quux,baz'
m.group()
中的参数,还可以传入多个,如下所示:
>>> m.groups()
('foo', 'quux', 'baz')
>>> m.group(2, 3)
('quux', 'baz')
>>> m.group(3, 2, 1)
('baz', 'quux', 'foo')
此时,能够按照参数的顺序和数值得到多个指定分组捕获,特别注意观察m.group(3, 2, 1)
的结果。
“向后引用”这个术语的英文是“backference”,很多中文资料翻译为“反向引用”,在本文中,我使用“向后引用”这个术语,原因在于这个翻译比较直白地反应了相关的效果。
所谓“向后引用”就是将前面的分组所捕获的结果向后再复制n个,比如:(\w+), \1
,第一个分组(\w+)
,后面的\1
表示将前面的分组捕获的结果在后面再次依样捕获1个。
>>> regex = r'(\w+),\1'
>>> m = re.search(regex, 'foo,foo')
>>> m
<_sre.SRE_Match object; span=(0, 7), match='foo,foo'>
>>> m.group(1)
'foo'
在这个示例中,分组(\w+)
已经捕获了字符串foo
,在正则表达式中的\1
表示向后引用前面的捕获结果,即可以继续在字符串中搜索,能够要再捕获一个foo
。再如:
>>> m = re.search(regex, 'qux,qux')
>>> m
<_sre.SRE_Match object; span=(0, 7), match='qux,qux'>
>>> m.group(1)
'qux'
如果搜索的字符串是foo,qux
,由于捕获第一个foo
之后,继续在字符串中搜索,无法得到foo
了,所以,会搜索失败。
>>> m = re.search(regex, 'foo,qux')
>>> print(m)
None
参考资料:https://realpython.com/regex-python/