2019-03-11
Python
Python re
模块提供了正则表达式的 API。关于 Python 支持的正则表达式语法,这里不介绍,仅介绍如何使用这些 API。
1. 编译正则表达式
正则表达式通过 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 语言简单了一些,但实际上引入了下面一个问题。
2. 讨厌的反斜杠
在正则表达式中,反斜杠 \
有着特殊含义,例如 \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" |
3. 匹配
当你编译好一个模式对象后,可以使用它的一些最常用的方法来进行你需要的匹配操作,如下表:
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)
4. 纯函数
上面介绍的都是 re 模块提供的 OOP 的接口,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 对象的方法。
5. 编译时的 flags
re 模块提供了一些编译选项,用来调整编译的特性。这些 flags 可以用“按位或” |
来组合,也支持缩写。支持的选项有:
Flag | Meaning |
---|---|
ASCII , A |
使得某些特殊字符如\w , \b ,\s 和 \d 仅仅匹配 ASCII 字符 |
DOTALL , S |
使得 . 匹配任何字符,包括换行符 |
IGNORECASE , I |
忽略大小写 |
LOCALE , L |
匹配时注意地区 |
MULTILINE , M |
多行匹配,影响^ 和$ |
VERBOSE , X 意为 extended |
可视化,允许在正则表达式中加入注释 |
(本文完)