上一节介绍了re模块中常用函数的用法,我们可以通过使用re模块和正则表达式对文本进行解析并获取值。当并未真正获取到search()、match()等函数匹配到的字符串。本节将会介绍如何获取匹配到的字符串。
匹配对象和编组
在模块re中,查找与模式匹配的子串的函数都在找到时返回MatchObject对象。这种对象包含与模式匹配的子串的信息,还包含模式的哪部分与子串的哪部分匹配的信息。这些子串部分称为编组(group)。
编组就是放在圆括号内的子模式,它们是根据左边的括号数编号的,其中编组0指的是整个模式。因此,在下面的模式中:
'There (was a (wee) (cooper)) who (lived in Fyfe)'
包含如下编组:
0 There was a wee cooper who lived in Fyfe
1 was a wee cooper
2 wee
3 cooper
4 lived in Fyfe
通常,编组包含诸如通配符和重复运算符等特殊字符,因此你可能想知道与给定编组匹配的内容。re模块为我们提供了匹配对象的一些重要的方法,用于获取编组对应的值。具体如下表所示:
方法 | 描述 |
group([group1, ...]) | 获取与给定子模式(编组)匹配的子串 |
start([group]) | 返回与给定编组匹配的子串的起始位置 |
end([group]) | 返回与给定编组匹配的子串的终止位置(与切片一样,不包含终止位置) |
span([group]) | 返回与给定编组匹配的子串的起始和终止位置 |
group():返回与模式中给定编组匹配的子串。如果没有指定编组号,则默认为0。如果只指定了一个编组号(或使用默认值0),将只返回一个字符串;否则返回一个元组,其中包含与给定编组匹配的子串。
start():返回与给定编组(默认为0,即整个模式)匹配的子串的起始索引。
end():与start()类似,但返回终止索引加1。
span():返回一个元组,其中包含与给定编组(默认为0,即整个模式)匹配的子串的起始索引和终止索引。
上面关于匹配对象方法的用法如下my_re_continue.py程序所示:
my_re_continue.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
string = 'www.guanhu.name'
# group()方法,获取与给定子模式(编组)匹配的子串
m = re.match(r'www\.(.*)\..{4}', string)
print('m = {}'.format(m))
print(m.group(1))
# start()方法,返回与给定编组匹配的子串的起始索引
print(m.start(1))
# end()方法
print(m.end(1))
# span()方法
print(m.span(1))
程序执行结果如下所示:
m = <re.Match object; span=(0, 15), match='www.guanhu.name'>
guanhu
4
10
(4, 10)
替换中的组号和函数
在上一节的示例程序中,我们使用re.sub()函数,将一个字串替换为了另一个字串。当然这也可以使用字符串中的replace()方法轻松完成。但是,正则表达式很有用,它能够让我们以更灵活的方式进行检索,还能够执行更复杂的替换。
为利用re.sub的强大功能,最简单的方式是在替代字符串中使用组号。在替换字符串中,任何类似于'\\n'的转义序列都将被替换为与模式中编组n匹配的字符串。例如,假设要将'*something*'替换为'<em>something</em>',其中前者是在纯文本文档(如电子邮件)表示突出的普通方式,而后者是相应的HTML代码(用于网页中)。下面先创建一个正则表达式,具体如下所示:
>>> pat = r'\*([^\*]+)\*'
创建模式之后,我们就可以使用re.sub来完成所需的替换了,具体如下所示:
>>> re.sub(pat, r'<em>\1</em>', string)
'<em>something</em>'
简短的两行代码,就将纯文本转换成了网页所需的HTML代码。通过使用函数来替换内容,可执行更复杂的替换。在这里,re.sub()这个函数将MatchObject作为唯一的参数,它返回的字符串即为替换后的内容。我们可以对匹配的字符串做任何处理,并通过细致的处理来生成替换内容。
贪婪模式和非贪婪模式
重复运算默认都是贪婪的,因此,它们将会匹配尽可能多的内容。重写前面的突出程序,在其中使用了如下模式:
>>> pat = r'\*(.+)\*'
这个模式与以星号打头并以星号结尾的内容匹配,这看起来毫无破绽,但是实际情况并非如此,如下所示:
>>> re.sub(pat, r'<em>\1</em>', '*This* is *it*!')
'<em>This* is *it</em>!'
从上面的执行结果,可以看出这个模式匹配了从第一个星号开始到最后一个星号之间的全部内容,其中包括了另外两个星号!这就是贪婪的意思:能匹配多少就匹配多少。
显然,这并不是我们想要的结果,当我们知道不应该将某个特定的字符包含在内时,使用本节前面的解决方案能够很好的解决我们的问题。但是,如果我们要使用'**something**'来表示突出内容呢?在这种情形下,在要强调的内容中包含单个星号不是问题,但是如何避免过度贪婪呢?
这实际上很容易,只需使用重复运算符的非贪婪版即可。对于所有的重复运算符,都可在后面加上问号来将其指定为非贪婪的。
>>> re.sub(r'\*\*(.+?)\*\*', r'<em>\1</em>', '**some** **thing**')
'<em>some</em> <em>thing</em>'
这里使用的是运算符+?而不是+。这意味着与以前一样,这个模式将匹配一个或多个通配符,但匹配尽可能少的内容,因为它是非贪婪的。因此,这个模式只匹配到下一个'\*\*',即它末尾的内容。
总结
本节主要介绍了以下内容:
- 匹配对象和编组。
- 匹配对象的方法。
- 如何通过匹配对象的方法以及编组获取匹配到的字符串。
- 使用re.sub()函数进行内容替换。
- 贪婪模式和非贪婪模式。
下一节将会介绍一些实例来巩固正则表达式以及re模块中常用的方式和方法,敬请关注!
创作不容易,还请点个免费赞!喜欢的小伙伴请点关注、收藏!欢迎大家转发、评论!