掌握正则表达式方法可以高效处理各种文本处理任务, 从简单的字符串验证到复杂的数据提取都能胜任。
贪心和非贪心匹配
在字符串 'HaHaHaHaHa'
中,
因为 (Ha){3,5}
可以匹配3个、4个或5个实例,
那么为什么在前面花括号的例子中,
Match
对象的group()
调用会返回 'HaHaHaHaHa'
,
而不是更短的可能结果。毕竟,
'HaHaHa'
和'HaHaHaHa'
也能够有效地匹配正则表达式 (Ha){3,5}
。
Python 的正则表达式默认是“贪心”的, 这表示在有二义的情况下, 它们会尽可能匹配最长的字符串。 花括号的“非贪心”版本匹配尽可能最短的字符串, 即在结束的花括号后跟着一个问号。
在交互式环境中输入以下代码, 注意在查找相同字符串时, 花括号的贪心形式和非贪心形式之间的区别:
import re
greedyHaRegex = re.compile(r'(Ha){3,5}')
mo1 = greedyHaRegex.search('HaHaHaHaHa')
print(mo1.group())
HaHaHaHaHa
nongreedyHaRegex = re.compile(r'(Ha){3,5}?')
mo2 = nongreedyHaRegex.search('HaHaHaHaHa')
print(mo2.group())
HaHaHa
请注意,问号在正则表达式中可能有两种含义: 声明非贪心匹配或表示可选的分组。这两种含义是完全无关的。
import re
phoneNumberRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
mo1=phoneNumberRegex.search('Cell:415-555-9999 Work:212-555-0000')
mo1.group()
'415-555-9999'
另一方面, findall()
不是返回一个 Match
对象,
而是返回一个字符串列表,只要在正则表达式中没有分组。
列表中的每个字符串都是一段被查找的文本,
它匹配该正则表达式。在交互式环境中输入以下代码:
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') #has no groups
phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']
如果在正则表达式中有分组,
那么 findall
将返回元组的列表。
每个元组表示一个找到的匹配,
其中的项就是正则表达式中每个分组的匹配字符串。
为了看看 findall()
的效果,
请在交互式环境中输入以下代码(请注意,被编译的正则表达式现在有括号分组):
phoneNumRegex =re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)') # has groups >>>
phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
[('415', '555', '9999'), ('212', '555', '0000')]
作为 findall()
方法的返回结果的总结,
请记住下面两点:
如果调用在一个没有分组的正则表达式上, 例如
\d\d\d-\d\d\d-\d\d\d\d
, 方法findall()
将返回一个匹配字符串的列表, 例如['415','555','1122'),('212-555-0000']
。如果调用在一个有分组的正则表达式上,例如
(\d\d\d)-(\d\d\d)-(\d\d\d\d)
, 方法findall()
将返回一个字符串的元组的列表 (每个分组对应一个字符串), 例如[(’415', '555', '1122'), ('212', '555', '0000')]
。
import re
regexl = re.compile('RoboCop')
regex2 = re.compile('R0B0C0P')
regex3 = re.compile('robOcop')
regex4 = re.compile('RobocOp')
但是,有时候只关心匹配字母,不关心他们是小写或大写,
要让正则表达式不区分大小写,
可以向 re.compile()
传入 re.IGNORECASE
或 re.I
,作为第二个参数。
robocop =re.compile(r'robocop', re.I)
robocop.search('RoboCop is part man, part machine, allcop.').group()
'RoboCop'
robocop.search('ROBOCOP protects the innocent.').group()
'ROBOCOP'
robocop.search('Al, why does your programming book talk about robocop so much?').group()
'robocop'
import re
phoneRegex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')
将正则表达式放在多行中,并加上注释, 像这样:
phoneRegex = re.compile(r'''(
(\d{3}|\(\d{3}\))? # area code
(\s|-|\.)? # separator
\d{3} # first 3 digits
(\s|-|\.) # separator
\d{4} # last 4 digits
(\s*(ext|x|ext.)\s*\d{2,5})? # extension
)''',re.VERBOSE)
请注意,前面的例子使用了三重引号 ( '''
) ,
创建了一个多行字符串。
这样就可以将正则表达式定义放在多行中,
让它更可读。
正则表达式字符串中的注释规则,
与普通的Python代码一样:#
符号和它后面直到行末的内容,
都被忽略。而且,表示正则表达式的多行字符串中,
多余的空白字符也不认为是要匹配的文本模式的一部分。
这能够组织正则表达式,让它更可读。
import re
someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL)
使用第二个参数的全部3个选项,看起来像这样:
someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL | re.VERBOSE)
可以向第二个参数传入其他选项,它们不常用,也可以在前面的资源中找到有关它们的信息。