正则表达式的使用
学习过最简单的正则表达式的规则后,如何在实际中运用呢?Python中的re模块提供了正则表达式引擎的接口,允许用户将表达式编译成对象,然后再进行匹配。
正则表达式的编译
将正则表达式编译成格式对象,此对象含有不同的操作函数,如查找或子串替换等。
>>> import re
>>> p = re.compile('ab*')
>>> print p
<_sre.SRE_Pattern object at 0x...>
Re.compile()也可以接受一个可选的参数flag,来选择不同的特殊功能和语法变体。我们先运行一个例子,再来讲解flag的设定:
>>> p = re.compile('ab*', re.IGNORECASE)
将正则表达式样式作为一个字符串传递给re.compile(),这是由于正则表达式不是Python的核心功能,所以也没有它的特殊语法(有些程序根本用不到正则表达式的功能,因而没有必要将它放进核心语法中),但是re模块作为C语言的扩展被包含到Python的模块中,类似的还有socket或zlib模块。
将正则表达式样式设计为字符串可以使Python语言更加简单,但是它有一个缺点,将在下一节讲解。
反斜杠\问题
如前所述,正则表达式使用反斜杠来表明特殊形式或取消特殊意义,而Python在字符串中同样使用反斜杠来实现相同意图,从而带来了冲突。
比如说你想写出一个正则表达式样式来匹配LaTeX中的\section,首先必须写出相应的样式,然后必须在每一个反斜杠前再加一个反斜杠来,如\\section,这个结果将传递给re.compile。然而,为了表示这是一个Python的字符串,每一个反斜杠必须再加一个反斜杠!
字符 | 状态 |
\section | 想要匹配的字符 |
\\section | 为re.compile()去掉反斜杠 |
“\\\\section” | 再加一个反斜杠来表明它是一个字符串而非有特殊意义的单词 |
简而言之,对了匹配一个无任何特殊意义的反斜杠,必须为正则表达式样式写上\\\\,因为正则表达式必须为\\,而Python字符串中的每一个无特殊含义的反斜杠都必须以\\来代替。在正则表达式中,使用反斜杠的情况常常碰见,这将导致无数多数的反复输入反斜杠,从而造成最终的字符串难于理解。
解决方法是使用Python的未转义的字符串声明:在以r为前缀的字符串中,反斜杠解除特殊含义,如r"\n"是一个包含两个字符的字符串,而"\n"则是一个字符表示换行。所以在Python代码中要使用这种表示方法来书写正则表达式。
正则表达式的样式 | 对应的未经转义的字符串 |
“ab*” | r”ab*” |
“\\\\section” | r”\\section” |
“\\w+\\s+\\s” | r”\w+\s+\1” |
匹配
当你有一个表示正则表达式的对象时,应当如何使用?样式对象有不同的方法和属性,这里只列出了最为常用的几个,包含全部方法和属性的列表请参考re的帮助文档。
方法或属性 | 用途 |
match() | 检查是否在字符串的开始处匹配 |
search() | 在字符串中扫描并寻找匹配的位置 |
findall() | 寻找所有匹配的子串,并将结果返回到一个列表中 |
finditer() | 寻找所有匹配的子串,并将它们作为迭代器返回 |
如果没有匹配成功,则match()和search()返回None;如果成功,将返回一个MatchObject对象,包含了匹配开始和结束的信息、匹配的子串等等。
可以通过re模块来学习匹配的使用。如果你的系统安装有Tkinter,也可以查看Tools/scripts/redemo.py,它是Python发行版中包含的一个演示程序,允许你输入正则表达式和字符串并进行匹配。该演示程序在进行调试复杂的正则表达式程序时非常有用。Phil Schwartz的Kodos也是一个用于正则表达式开发和测试的交互式工具。
本文档使用Python标准的解释器来完成实例。首先,打开Python解释器,导入re模块,对正则表达式进行编译:
Python 2.2.2 (#1, Feb 10 2003, 12:57:01)
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
<_sre.SRE_Pattern object at 0x...>
现在你可以针对正则表达式[a-z]+尝试匹配不同的字符串。一个空串肯定不会匹配,因为+表示匹配一个或多个,这种情况下调用match()应当返回None,这时Python解释器不会有任何输出,你可以通过显式的打印出匹配的结果来查看。
>>> p.match("")
>>> print p.match("")
None
接着来尝试一个可以匹配的字符串,这里match()将返回一个MatchObject,你可以将这个对象存储在变量中以便以后使用。
>>> m = p.match('tempo')
>>> print m
<_sre.SRE_Match object at 0x...>
可以请求MatchObject来查看匹配信息,它也包含不同的方法和属性,最重要的有:
方法或属性 | 用途 |
group() | 返回匹配的字符串 |
start() | 返回匹配的开始位置(下标) |
end() | 返回匹配的结束位置 |
span() | 返回一个元组,它包含匹配的(开始,结束)的位置 |
尝试以下方法将阐明上面这些方法的意思:
>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)
由于match()只检查字符串的起始位置,start()一定为0,但search()方法将遍历整个字符串,所以匹配可能不在起始位置。
>>> print p.match('::: message')
None
>>> m = p.search('::: message') ; print m
<_sre.SRE_Match object at 0x...>
>>> m.group()
'message'
>>> m.span()
(4, 11)
在实际的程序中,最常用的做法是将MatchObject存储在变量中,然后查检是否为None,例如:
p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
print 'Match found: ', m.group()
else:
print 'No match'
两个成员函数将返回所有的匹配,其中一个是findall(),它返回匹配字符串的列表:
>>> p = re.compile('\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']
findall()必须在返回结果前创建整个列表,finditer()函数返回MatchObject对象序列的迭代器:
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator
<callable-iterator object at 0x401833ac>
>>> for match in iterator:
... print match.span()
...
(0, 2)
(22, 24)
(29, 31)