正则表达式(称为RE,或正则,或正则表达式模式)本质上是嵌入在Python中的一种微小的、高度专业化的编程语言,可通过 re 模块获得。
简单模式
正则表达式用于对字符串进行操作,因此我们将从最常见的任务开始:匹配字符
匹配字符:匹配不同的字符集合是正则表达式可以做的第一件事
元字符是 [ 和 ]
[abc],[a-c],[0-9],[abc作为元字符在中括号内失效,[^5] 匹配非'5'的字符,
[5^] 将匹配 '5' 或 '^'
元字符是反斜杠,\
反斜杠来移除它们的特殊含义:\[ 或 \\,
以 '\' 开头的特殊序列表示通常有用的预定义字符集
-
\d匹配任何十进制数字;这等价于类
[0-9]。 -
\D匹配任何非数字字符;这等价于类
[^0-9]。 -
\s匹配任何空白字符;这等价于类
[ \t\n\r\f\v]。转义符在中括号中不失效吗 -
\S匹配任何非空白字符;这相当于类
[^ \t\n\r\f\v]。 -
\w匹配任何字母与数字字符;这相当于类
[a-zA-Z0-9_]。 -
\W匹配任何非字母与数字字符;这相当于类
[^a-zA-Z0-9_]。
元字符是 .
它匹配除换行符之外的任何内容,并且有一个可选模式( re.DOTALL )甚至可以匹配换行符
重复:指定正则的某些部分必须重复一定次数
元字符是 *
一个逐步的例子将使这更加明显。 让我们考虑表达式 a[bcd]*b。 这个正则匹配字母 'a',类 [bcd] 中的零或多个字母,最后以 'b' 结尾。 现在想象一下这个正则与字符串 'abcbd' 匹配。
| 步骤 | 匹配 | 说明 |
|---|---|---|
| 1 | a |
正则中的 a 匹配。 |
| 2 | abcbd |
引擎尽可能多地匹配 [bcd]* ,直到字符串结束。 |
| 3 | 失败 | 引擎尝试匹配 b ,但是当前位置位于字符串结束,所以匹配失败。 |
| 4 | abcb |
回退一次,[bcd]* 少匹配一个字符。 |
| 5 | 失败 | 再次尝试匹配 b , 但是当前位置是最后一个字符 'd' 。 |
| 6 | abc |
再次回退,所以 [bcd]* 只匹配 bc 。 |
| 6 | abcb |
再试一次 b 。 这次当前位置的字符是 'b' ,所以它成功了。 |
元字符是 +
ca+t 将匹配 'cat' (1 个 'a'),'caaat' (3 个 'a'),但不会匹配 'ct'
重复限定符。 问号字符 ? 匹配一次或零次
home-?brew 匹配 'homebrew' 或 'home-brew'
重复限定符是 {m,n}
a/{1,3}b 将匹配 'a/b' ,'a//b' 和 'a///b' 。 它不匹配没有斜线的 'ab',或者有四个的 'a////b', {0,} 与 * 相同, {1,} 相当于 + , {0,1} 和 ? 相同。 最好使用 * , + 或 ? ,只要因为它们更短更容易阅读。
使用正则表达式
re 模块提供了正则表达式引擎的接口,允许你将正则编译为对象,然后用它们进行匹配
编译正则表达式
正则表达式被编译成模式对象,模式对象具有各种操作的方法,例如搜索模式匹配或执行字符串替换
p = re.compile('ab*')
re.compile() 也接受一个可选的 flags 参数,用于启用各种特殊功能和语法变体, p = re.compile('ab*', re.IGNORECASE)
正则作为字符串传递给 re.compile() ,将正则放在字符串中可以使 Python 语言更简单,但有一个缺点:反斜杠灾难。解决方案 r"\n" 是一个包含 '\' 和 'n' 的双字符字符串,而 "\n" 是一个包含换行符的单字符字符串。
应用匹配
一旦你有一个表示编译正则表达式的对象,你用它做什么? 模式对象有几种方法和属性。
| 方法 / 属性 | 目的 |
|---|---|
match() |
确定正则是否从字符串的开头匹配。 |
search() |
扫描字符串,查找此正则匹配的任何位置。 |
findall() |
找到正则匹配的所有子字符串,并将它们作为列表返回。 |
finditer() |
找到正则匹配的所有子字符串,并将它们返回为一个 iterator。 |
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')
>>> p.match("")
>>> print(p.match(""))
None
>>> m = p.match('tempo')
>>> m
<re.Match object; span=(0, 5), match='tempo'>
现在你可以检查 匹配对象 以获取有关匹配字符串的信息。 匹配对象实例也有几个方法和属性;最重要的是:
| 方法 / 属性 | 目的 |
|---|---|
group() |
返回正则匹配的字符串 |
start() |
返回匹配的开始位置 |
end() |
返回匹配的结束位置 |
span() |
返回包含匹配 (start, end) 位置的元组 |
尝试这些方法很快就会清楚它们的含义:
group() 返回正则匹配的子字符串。 start() 和 end() 返回匹配的起始和结束索引。 span() 在单个元组中返回开始和结束索引。 由于 match() 方法只检查正则是否在字符串的开头匹配,所以 start() 将始终为零。 但是,模式的 search() 方法会扫描字符串,因此在这种情况下匹配可能不会从零开始。:
>>>
>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m)
<re.Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
(4, 11)
在实际程序中,最常见的样式是在变量中存储 匹配对象,然后检查它是否为 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(r'\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']
在这个例子中需要 r 前缀,使字面为原始字符串字面,因为普通的“加工”字符串字面中的转义序列不能被 Python 识别为正则表达式,导致 DeprecationWarning 并最终产生 SyntaxError。 请参阅 反斜杠灾难。
findall() 必须先创建整个列表才能返回结果。 finditer() 方法将一个 匹配对象 的序列返回为一个 iterator
>>>
>>> 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)
模块级别函
>>> 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 '>
编译标志
这是一个可用标志表,以及每个标志的更详细说明。
| Flag | 含义 |
|---|---|
ASCII, A
|
使几个转义如 \w、\b、\s 和 \d 匹配仅与具有相应特征属性的 ASCII 字符匹配。 |
DOTALL, S
|
使 . 匹配任何字符,包括换行符。 |
IGNORECASE, I
|
进行大小写不敏感匹配。 |
LOCALE, L
|
进行区域设置感知匹配。 |
MULTILINE, M
|
多行匹配,影响 ^ 和 $。 |
VERBOSE, X (为 '扩展') |
启用详细的正则,可以更清晰,更容易理解。 |
re.I | re.M 设置 I 和 M 标志
这里的正则使用 re.VERBOSE;看看阅读有多容易?: charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE) 如果没有详细设置,正则将如下所示: charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")
更多元字符
Crow|Servo 将匹配 'Crow' 或 'Servo',而不是 'Cro'、'w' 或 'S' 和 'ervo'
print(re.search('^From', 'From Here to Eternity')) <re.Match object; span=(0, 4), match='From'> print(re.search('^From', 'Reciting From Memory')) None `
print(re.search('}$', '{block}')) <re.Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}\n')) <re.Match object; span=(6, 7), match='}'> `