5. 正则表达式分组

正则表达式的分组(Grouping)是指将一部分子模式用括号 ( ) 包围起来,使其成为一个独立的单元。

分组示例

先来创建一个字符串 s2 用来绑定一个字符串如下:

>>> import re
>>> s2 = 'wazwbzwcz wdz wmz w1z w8z w666z w z wAz'

使用分组提取 'w' 开头,'z' 结尾,中间是一个任意字符的字符串中间的那个一个字符。

>>> re.findall(r'w.z', s2)
['waz', 'wbz', 'wcz', 'wdz', 'wmz', 'w1z', 'w8z', 'w z', 'wAz']
>>> re.findall(r'w(.)z', s2)
['a', 'b', 'c', 'd', 'm', '1', '8', ' ', 'A']

可见 使用 w.z 时,没有分组返回的是包含 'w''z' 在内的所有匹配的三个字符。而使用 w(.)z 分组时返回的是组内匹配的1个字符。

接下来我们使用分组提取 'w' 开头,'z' 结尾,中间是一个任意字符的字符串首字母w和中间的那个一个字符组成的所有的字符串。

>>> re.findall(r'(w.)z', s2)
['wa', 'wb', 'wc', 'wd', 'wm', 'w1', 'w8', 'w ', 'wA']

此时 组内的内容 (w.) 匹配的内容都提取出来了。

当有两个或两个以上分组的情况

当然一个正则表达式中用两个或两个以上分组时,re.findall() 返回类的列表内是元组,每一个元组对应着一次成功匹配的数据,数据按分组的先后顺序摆放。

示例

>>> import re
>>> s3 = '<h1>咏鹅</h1><p>鹅,鹅,鹅,曲项向天歌。</p><p>白毛浮绿水,红掌拨清波。</p>'

我们提取 HTML 标签开始(如<h1>),标签内部的内容(如:咏鹅)和标签的结束标志(如:</h1>

>>> re.findall(r'<(.*?)>(.*?)<(.*?)>', s3)
[('h1', '咏鹅', '/h1'), ('p', '鹅,鹅,鹅,曲项向天歌。', '/p'), ('p', '白毛浮绿水,红掌拨清波。', '/p')]

上述正则表达式 r'<(.*?)>(.*?)<(.*?)>' 中有三个分组, 而返回的列表中也不再是字符串,而是元组,每个元组内有三个数据,对应分组的先后顺序。

反向引用

在一个正则表达式当中,如果实现了分组,那么每一组将有一个编号,这个编号是从1开始的,第一个组匹配的内容是1,第二个组匹配的内容是2,我们可以用这个编号反向引用原来的内容。

下列正则表达式中,正则表达式 r'<(.*?)>(.*?)<(.*?)>' 有三个组,则第一个组匹配的内容是 \1, 第二个组的内容为 \2, 以此类推。

>>> s3 = '<h1>咏鹅</h1><p>鹅,鹅,鹅,曲项向天歌。</p><p>白毛浮绿水,红掌拨清波。</p>'
>>> re.findall(r'<(.*?)>(.*?)<(.*?)>', s3)
[('h1', '咏鹅', '/h1'), ('p', '鹅,鹅,鹅,曲项向天歌。', '/p'), ('p', '白毛浮绿水,红掌拨清波。', '/p')]

下面我们使用反向引用,将 s3 字符串的 <h1> 标签和 <p> 标签都替换成 <h4> 标签,其他原标签内的内容不变。

注意:内容部分对应第二个分组,即 \2对应的内容

替换后结果如下:

>>> re.sub(r'<(.*?)>(.*?)<(.*?)>', r'<h4>\2<h4>', s3)
'<h4>咏鹅<h4><h4>鹅,鹅,鹅,曲项向天歌。<h4><h4>白毛浮绿水,红掌拨清波。<h4>'

注意: r'<h4>\2<h4>' 中的 \2 对应原来的内容。\1\3 在此处没有使用。

命名分组

(?P<name>...) 定义分组,通过名称(而非数字)引用分组,提高可读性。

>>> re.findall(r'<(.*?)>(?P<content>.*?)<(.*?)>', s3)
[('h1', '咏鹅', '/h1'), ('p', '鹅,鹅,鹅,曲项向天歌。', '/p'), ('p', '白毛浮绿水,红掌拨清波。', '/p')]

上述正则 r'<(.*?)>(?P<content>.*?)<(.*?)> 中,我们将第二个分组取了名字为 content, 后面可以使用这个名称进行反向引用。

反向引用时的语法格式是 \g<分组名称>, 如 '\g<content>'

我们将 s3 字符串的 <h1> 标签和 <p> 标签都替换成 <h4> 标签,其他原标签内的内容不变。这次我们使用命名分组。结果如下:

>>> re.sub(r'<(.*?)>(?P<content>.*?)<(.*?)>', r'<h4>\g<content></h4>', s3)
'<h4>咏鹅</h4><h4>鹅,鹅,鹅,曲项向天歌。</h4><h4>白毛浮绿水,红掌拨清波。</h4>'

至此,你已经学习了正则表达式最实用的语法了,下一节我们再学习一下附加的语法规则。