>>> from env_helper import info; info()
页面更新时间: 2024-03-29 09:21:12
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

1.3. 用正则表达式匹配更多模式

既然你已知道用 Python 创建和查找正则表达式对象的基本步骤, 就可以尝试一些更强大的模式匹配功能了。

1.3.1. 利用括号分组

假定想要将区号从电话号码中分离。添加括号将在正则表达式中创建“分组”: (\d\d\d)-(\d\d\d-\d\d\d\d) 。然后可以使用 group() 匹配对象方法, 从一个分组中获取匹配的文本。

正则表达式字符串中的第一对括号是第1组。第二对括号是第2组。 向 group()匹配对象方法传入整数1或2, 就可以取得匹配文本的不同部分。向 group() 方法 传入0或不传入参数,将返回整个匹配的文本。 在交互式环境中输入以下代码:

>>> import re
>>> phoneNumberRegex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
>>> mo=phoneNumberRegex.search('my phone number is 415-555-4242')
>>> mo.group(1)
'415'
>>> mo.group(2)
'555-4242'
>>> mo.group(0)
'415-555-4242'
>>> mo.group()
'415-555-4242'

如果想要一次就获取所有的分组,请使用 group() 方法,注意函数名的复数形式。

>>> mo.groups()
('415', '555-4242')
>>> areaCode,mainNumber=mo.groups()
>>> print(areaCode)
415
>>>  print (mainNumber)
555-4242

因为 mo.groups() 返回多个值的元组, 所以你可以使用多重复制的技巧, 每个值赋给一个独立的变量,就像前面的代码行: areaCode , mainNumber = mo.groups()

括号在正则表达式中有特殊的含义, 但是如果你需要在文本中匹配括号,怎么办? 例如,你要匹配的电话号码,可能将区号放在一对括号中。 在这种情况下,就需要用倒斜杠对(和)进行字符转义。 在交互式环境中输入以下代码:

>>> #phoneNumRegex = re.compile(r'(\(\d\d\d\)) (\d\d\d-\d\d\d\d)')
>>> phoneNumRegex = re.compile(r'(\(\d\d\d\)) (\d\d\d-\d\d\d\d)')
>>> mo = phoneNumRegex.search('My phone number is (415) 555-4242.')
>>> print(mo.group(1))
(415)
>>> mo.group(2)
'555-4242'

传递给 re.compile() 的原始字符串中,\(\)转义字符将匹配实际的括号字符。

1.3.2. 用管道匹配多个分组

字符 | 称为“管道”。希望匹配许多表达式中的一个时, 就可以使用它。例如,正则表达式 r'Batman|Tina Fey' 将匹配'Batman''Tina Fey'

如果 BatmanTina Fey 都出现在被查找的字符串中, 第一次出现的匹配文本,将作为 Match 对象返回。 在交互式环境中输入以下代码:

>>> heroRegex = re.compile (r'Batman|Tina Fey')
>>> mol = heroRegex.search('Batman and Tina Fey.')
>>> mol .group()
'Batman'
>>> mo2 = heroRegex.search('Tina Fey and Batman.')
>>> mo2. group ()
'Tina Fey'

注意 利用 fmdall() 方法,可以找到“所有”匹配的地方。 这在7.5节 “ fmdall() 方法”中讨论。

也可以使用管道来匹配多个模式中的一个,作为正则表达式的一部分。 例如,假设你希望匹配 'Batman''Batmobile''Batcopter''Batbat' 中任意一个。 因为所有这些字符串都以 Bat 开始, 所以如果能够只指定一次前缀,就很方便。 这可以通过括号实现。在交互式环境中输入以下代码:

>>> batRegex = re.compile(r'Bat(man|mobile|copter|bat)')
>>> mo = batRegex.search('Batmobile lost a wheel')
>>> mo.group()
'Batmobile'
>>> mo.group(1)
'mobile'

方法调用 mo.group() 返回了完全匹配的文本 'Batmobile' , 而 mo.group(l) 只是返回第一个括号分组内匹配的文本 'mobile' 。 通过使用管道字符和分组括号,可以指定几种可选的模式, 让正则表达式去匹配。

如果需要匹配真正的管道字符,就用倒斜杠转义,即\|

1.3.3. 用问号实现可选匹配

有时候,想匹配的模式是可选的。就是说,不论这段文本在不在, 正则表达式都会认为匹配。字符?表明它前面的分组在 这个模式中是可选的。例如,在交互式环境中输入以下代码:

>>> batRegex = re.compile(r'Bat(wo)?man')
>>> mol = batRegex.search('The Adventures of Batman')
>>> mol.group()
'Batman'
>>> mo2 = batRegex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'

正则表达式中的( wo )?部分表明,模式 wo 是可选的分组。 该正则表达式匹配的文本中, wo 将出现零次或一次。 这就是为什么正则表达式既匹配'Batwoman' , 又匹配'Batman'

利用前面电话号码的例子, 你可以让正则表达式寻找包含区号或不包含区号的 电话号码。在交互式环境中输入以下代码:

>>> phoneRegex =re.compile(r'(\d\d\d-)?\d\d\d-\d\d\d\d')
>>> mol = phoneRegex.search('My number is 415-555-4242')
>>> mol.group()
'415-555-4242'
>>> mo2 = phoneRegex.search('My number is 555-4242')
>>> mo2.group()
'555-4242'

你可以认为?是在说,“匹配这个问号之前的分组零次或一次”。

如果需要匹配真正的问号字符,就使用转义字符 \?

1.3.4. 用星号匹配零次或多次

* (称为星号)意味着“匹配零次或多次”, 即星号之前的分组,可以在文本中出现任意次。 它可以完全不存在,或一次又一次地重复。 让我们再来看看 Batman 的例子。

>>> batRegex = re.compile(r'Bat(wo)*man')
>>> mol = batRegex.search('The Adventures of Batwoman')
>>> mol.group()
'Batwoman'
>>> mo2 = batRegex.search('The Adventures of Batwoman')
>>> mo2.group()
'Batwoman'
>>> mo3 = batRegex.search('The Adventures of Batwowowowoman')
>>> mo3.group()
'Batwowowowoman'

对于 'Batman' , 正则表达式的(wo)*部分匹配 wo 的零个实例。 对于'Batwoman'(wo)*匹配 wo 的一个实例。 对于 'Batwowowowoman'(wo)* 匹配 wo 的4个实例。

如果需要匹配真正的星号字符, 就在正则表达式的星号字符前加上倒斜杠,即 \*

1.3.5. 用加号匹配一次或多次

* 意味着“匹配零次或多次”,+ (加号)则意味着 “匹配一次或多次”。星号不要求分组出现在匹配的字符串中, 但加号不同,加号前面的分组必须“至少出现一次”。 这不是可选的。在交互式环境中输入以下代码, 把它和前一节的星号正则表达式进行比较:

>>> batRegex = re.compile(r'Bat(wo)+man')
>>> mol = batRegex.search('The Adventures of Batwowoman')
>>> mol.group()
'Batwowoman'
>>> mo2 = batRegex.search('The Adventures of Batwowowowoman')
>>> mo2.group()
'Batwowowowoman'
>>> mo3 = batRegex.search('The Adventures of Batman')
>>> mo3 == None
True

正则表达式 Bat(wo)+man 不会匹配 字符串 'The Adventures of Batman', 因为加号要求 wo 至少出现一次。

如果需要匹配真正的加号字符, 在加号前面加上倒斜杠实现转义:\+

1.3.6. 用花括号匹配特定次数

如果想要一个分组重复特定次数, 就在正则表达式中该分组的后面, 跟上花括号包围的数字。例如, 正则表达式 (Ha){3}将匹配 字符串'HaHaHa',但不会匹配'HaHa', 因为后者只重复了 (Ha) 分组两次。

除了一个数字,还可以指定一个范围, 即在花括号中写下一个最小值、 一个逗号和一个最大值。例如, 正则表达式 (Ha){3,5} 将匹配'HaHaHa''HaHaHaHa''HaHaHaHaHa'

也可以不写花括号中的第一个或第二个数字, 不限定最小值或最大值。例如, (Ha){3,} 将匹配3次或更多次实例, (Ha){,5} 将匹配0到5次实例。 花括号让正则表达式更简短。这两个正则表达式匹配同样的模式:

(Ha){3}
(Ha)(Ha)(Ha)

这两个正则表达式也匹配同样的模式:

(Ha){3,5}
((Ha)(Ha)(Ha))|((Ha)(Ha)(Ha)(Ha))|((Ha)(Ha)(Ha)(Ha)(Ha))

在交互式环境中输入以下代码:

>>> haRegex = re.compile(r'(Ha){3}')
>>> mol = haRegex.search('HaHaHa')
>>> mol.group()
'HaHaHa'
>>> mo2 = haRegex.search('Ha')
>>> mo2 == None
True

这里, (Ha){3} 匹配'HaHaHa', 但不匹配'Ha'。因为它不匹配'Ha' , 所以 search() 返回 None