>>> from env_helper import info; info()
页面更新时间: 2024-03-29 09:20:56
运行环境:
Linux发行版本: Debian GNU/Linux 12 (bookworm)
操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
Python版本: 3.11.2
1.5. 正则表达式方法详解¶
1.5.1. 贪心和非贪心匹配¶
在字符串 '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
请注意,问号在正则表达式中可能有两种含义: 声明非贪心匹配或表示可选的分组。这两种含义是完全无关的。
1.5.2. findall()
方法¶
除了search方法外,Regex
对象也有一个 fmdall()
方法。
search()
将返回一个Match
对象,
包含被查找字符串中的“第一次”匹配的文本, 而 findall()
方法将返回一组字符串, 包含被查找字符串中的所有匹配。 为了看看
search()
返回的 Match
对象只 包含第一次出现的匹配文本,
请在交互式环境中输入以下代码:
>>> 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()
方法的返回结果的总结, 请记住下面两点:
1.如果调用在一个没有分组的正则表达式上, 例如 \d\d\d-\d\d\d-\d\d\d\d
, 方法 findall()
将返回一个匹配字符串的列表,
例如['415','555','1122'),('212-555-0000']
。
2.如果调用在一个有分组的正则表达式上,例如
(\d\d\d)-(\d\d\d)-(\d\d\d\d)
, 方法 findall()
将返回一个字符串的元组的列表 (每个分组对应一个字符串),
例如[(’415', '555', '1122'), ('212', '555', '0000')]
。
1.5.3. 不区分大小写的匹配¶
通常,正则表达式用你指定的大小写匹配文本。例如, 下面的正则表达式匹配完全不同的字符串:
>>> 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'
1.5.4. 管理复杂的正则表达式¶
如果要匹配的文本模式很简单,正则表达式就很好。
但匹配复杂的文本模式,可能需要长的、费解的正则表达式。 你可以告诉
rexompile()
,忽略正则表达式字符串中的
空白符和注释,从而缓解这一点。要实现这种详细模式, 可以向
rexompile()
传入变量 re.VERBOSE
,作为第二个参数。
现在,不必使用这样难以阅读的正则表达式:
>>> 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代码一样:#
符号和它后面直到行末的内容,
都被忽略。而且,表示正则表达式的多行字符串中,
多余的空白字符也不认为是要匹配的文本模式的一部分。
这让你能够组织正则表达式,让它更可读。
1.5.5. 组合使用 re.IGNOREC ASE
、 re.DOTALL
和 re.VERBOSE
¶
如果你希望在正则表达式中使用 re.VERBOSE
来编写注释,还希望使用
re.IGNORECASE
来忽略大小写,该怎么办?遗憾的是, re.compile()
函数只接受一个值作为它的第二参数。可以使用管道字符(|
)将变量组合起来,从而绕过这个限制。管道字符在这里称为“按位或”操作符。
所以,如果希望正则表达式不区分大小写, 并且句点字符匹配换行,
就可以这样构造 re.compile()
调用:
>>> import re
>>> someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL)
使用第二个参数的全部3个选项,看起来像这样:
>>> someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL | re.VERBOSE)
可以向第二个参数传入其他选项,它们不常用,但你也可以在前面的资源中找到有关它们的信息。