[ Python  ]

Python 中正则表达式 API 简介

Python re 模块提供了一套正则表达式引擎的接口,可以将正则表达式编译成 pattern 对象,然后对他们执行匹配操作。关于 Python 支持的正则表达式语法,这里不介绍,仅介绍如何使用这些 API。

编译正则表达式

正则表达式通过 re.compile() 编译成模式(pattern)对象,模式对象支持匹配、搜索和替换等操作。

>>> import re
>>> p = re.compile('ab*')
>>> p
re.compile('ab*')

re.compile() 可以传入额外的 flag 参数,用于设置额外的特性,如忽略大小写:

>>> p = re.compile('ab*', re.IGNORECASE)

正则表达式以字符串的形式被传入 re.compile(),因为正则表达式并不是 Python 语言的一部分,只是通过 re 模块实现了(实际上 re 模块是一个 C 扩展模块,其他的 C 扩展模块还有 socket 和 zlib 等)。

使用字符串来表示正则表达式使得 Python 语言简单了一些,但实际上引入了下面一个问题。

讨厌的反斜杠

在正则表达式中,反斜杠 \ 有着特殊含义,例如 \s 代表空白。但是在 Python 字符串中,反斜杠也有特殊作用,它们会和后面的字符形成转义字符

比如你要写一个匹配字符串 \section 的正则表达式,那么这个正则表达式应为 \\section(要把第一个反斜杠转义)。但如果要把它放进 Python 的字符串中,你又要把这两个反斜杠都转义才行——\\\\section。看起来十分丑陋,对吧。

所以最好的方法就是使用 raw-string 来使得 Python 不要对反斜杠进行转义,那么上面的正则表达式就可以写为 r"\\section"

Regular String Raw String
"ab*" r"ab*"
"\\\\section" r"\\section"
"\\w+\\s+\\1" r"\w+\s+\1"

匹配

当你编译好一个模式对象后,可以使用它的一些最常用的方法来进行你需要的匹配操作,如下表:

Method Purpose
match() 判定一个字符串的开头是否匹配该正则表达式
search() 扫描一遍字符串,找到匹配的地方
findall() 找到字符串所有匹配的子串,并且以列表的形式返回
finditer() 找到字符串所有匹配的子串,并且以迭代器的形式返回

match()search() 如果没有匹配到,则会返回 None。如果成功匹配,则会返回一个 match 对象,包含这次匹配的信息:从哪里开始、从哪里结束、匹配的子串等等。

注:在 CPython 发行版的 Tools/demo/redemo.py 文件可以用图形界面测试和 Debug 正则表达式。

匹配示例:

>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')

>>> m = p.match('tempo')
>>> m
<re.Match object; span=(0, 5), match='tempo'>

现在你可以通过 match 对象来获知本次匹配的信息。match 对象支持以下方法:

Method Purpose
group() 返回匹配的字符子串
start() 返回匹配开始的下标
end() 返回匹配结束的下标
span() 返回匹配位置的下标元组(开始,结束)

示例:

>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)

因为 match() 是匹配字符串开头的,所以它的结果的 start() 总是 0;而 search() 则扫描一遍字符串,找到第一个匹配的地方。

在日常应用中,最常用的做法是将 match 对象存在变量中,然后判断这个变量是否为 None,如下:

p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
    print('Match found: ', m.group())
else:
    print('No match')

match()search() 都只能匹配一个地方。使用 findall()finditer() 可以返回字符串中所有的匹配。注意:findall() 返回的是字符串列表,而 finditer() 返回的是 match 对象的迭代器。例如:

>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator  
<callable_iterator object at 0x...>
>>> for match in iterator:
...     print(match.span())
...
(0, 2)
(22, 24)
(29, 31)

模块级函数

re 模块提供了同名的模块级函数 match()search()findall()sub() 等。这些函数的第一个参数为一个表示正则表达式的字符串,第二个参数是要匹配的字符串。例如:

>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')  
<re.Match object; span=(0, 5), match='From '>

事实上这些函数的内部也是创建了一个临时的 pattern 对象,并且调用 pattern 对象的方法。

编译时的 flags

re 模块提供了一些编译选项,用来调整编译的特性。这些 flags 可以用“按位或” | 来组合,也支持缩写。支持的选项有:

Flag Meaning
ASCII, A 使得某些特殊字符如\w, \b\s\d 仅仅匹配 ASCII 字符
DOTALL, S 使得 . 匹配任何字符,包括换行符
IGNORECASE, I 忽略大小写
LOCALE, L 匹配时注意地区
MULTILINE, M 多行匹配,影响^$
VERBOSE, X 意为 extended 可视化,允许在正则表达式中加入注释

(本文完)