Python正则表达式

本文详细介绍Python中正则表达式的使用方法,包括基本步骤、高级匹配技巧、字符分类及通配符等,并通过示例代码展示如何进行文本匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1. 使用Python正则表达式的步骤

正则表达式,简称为 regex,是文本模式的描述方法。正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。

在 Python 中使用正则表达式有如下几个步骤:
1.用 import re 导入正则表达式模块。
2.用 re.compile()函数创建一个 Regex 对象(强烈建议使用Python的r前缀,就不用考虑转义的问题了)。
3.向 Regex 对象的 search()方法传入想查找的字符串。它返回一个 Match 对象。
4.调用 Match 对象的 group()方法,返回实际匹配文本的字符串。

1.1 创建正则表达式对象

Python 中所有正则表达式的函数都在 re 模块中。在交互式环境中输入以下代码,导入该模块:

>>> import re

re.compile()传入一个字符串值,表示正则表达式,它将返回一个 Regex 模式对象。

>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')

现在 phoneNumRegex 变量包含了一个 Regex 对象。

1.2 匹配 Regex 对象

Regex 对象的 search()方法查找传入的字符串,如果字符串中没有找到该正则表达式模式,search()方法将返回 None。如果找到了该模式,search()方法将返回一个 Match 对象。Match 对象有一个 group()方法,它返回被查找字符串中实际匹配的文本。例如,在交互式环境中输入以下代码:

>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')  # 要匹配的模式
>>> mo = phoneNumRegex.search('My number is 415-555-4242.') # 查找待匹配字符串里面是否有符合的字串
>>> print('Phone number found: ' + mo.group())
Phone number found: 415-555-4242

我们将期待的模式传递给 re.compile(),并将得到的 Regex 对象保存在phoneNumRegex 中。然后我们在 phoneNumRegex 上调用 search(),向它传入想查找的字符串,查找的结果保存在变量 mo 中。然后在 mo 变量上调用 group(),返回匹配的结果。将 mo.group()写在打印语句中,显示出完整的匹配,即 415-555-4242。

通过在字符串的第一个引号之前加上 r,可以将该字符串标记为原始字符串,它不包括转义字符。例如输 入r'\d\d\d-\d\d\d-\d\d\d\d' , 比 输入'\\d\\d\\d-\\d\\d\\d-\\d\\d\\d\\d'要容易得多。


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

常用正则表达式符号:

在这里插入图片描述

2.1 利用括号分组

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

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

>>> phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') 
>>> mo = phoneNumRegex.search('My 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'

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

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

2.2 用管道匹配多个分组

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

如果 Batman 和 Tina Fey 都出现在被查找的字符串中,第一次出现的匹配文本将作为 Match 对象返回

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

2.3 用问号匹配零次或一次

问号?意味着匹配这个问号之前的分组零次或一次。

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

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

2.4 用星号匹配零次或多次

星号*意味着“匹配零次或多次”,即星号之前的分组,可以在文本中出现任意次。它可以完全不存在,或一次又一次地重复。

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

2.5 用加号匹配一次或多次

加号+意味着“匹配一次或多次”,加号前面的分组必须“至少出现一次”。

>>> batRegex = re.compile(r'Bat(wo)+man') 
>>> mo1 = batRegex.search('The Adventures of Batwoman')
>>> mo1.group()
'Batwoman'
>>> 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 至少出现一次

2.6 用花括号匹配特定次数

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

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

也可以不写花括号中的第一个或第二个数字,不限定最小值或最大值。例如,(Ha){3,}将匹配 3 次或更多次实例,(Ha){,5}将匹配 0 到 5 次实例。

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

3. 贪心和非贪心匹配

Python 的正则表达式默认是“贪心”的,这表示在有二义的情况下,它们会尽可能匹配最长的字符串。花括号的“非贪心”版本匹配尽可能最短的字符串,即在结束的花括号后跟着一个问号

在交互式环境中输入以下代码,注意在查找相同字符串时,花括号的贪心形式和非贪心形式之间的区别:

>>> greedyHaRegex = re.compile(r'(Ha){3,5}')
>>> mo1 = greedyHaRegex.search('HaHaHaHaHa')
>>> mo1.group()
'HaHaHaHaHa'
>>> nongreedyHaRegex = re.compile(r'(Ha){3,5}?')
>>> mo2 = nongreedyHaRegex.search('HaHaHaHaHa')
>>> mo2.group() 
'HaHaHa'

4. findall()方法

除了search()方法外,Regex对象也有一个findall()方法。search()将返回一个Match对象,包含被查找字符串中的“第一次”匹配的文本,而 findall()方法将返回一组字符串,包含被查找字符串中的所有匹配。

search()返回的 Match 对象只包含第一次出现的匹配文本:

>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
>>> mo = phoneNumRegex.search('Cell: 415-555-9999 Work: 212-555-0000')
>>> mo.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 将返回元组的列表。每个元组表示一个找到的匹配,其中的项就是正则表达式中每个分组的匹配字符串:

>>> 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', '1122'), ('212', '555', '0000')]

作为 findall()方法的返回结果的总结,请记住下面两点:
1.如果调用在一个没有分组的正则表达式上,例如\d\d\d-\d\d\d-\d\d\d\d,方法findall()将返回一个匹配字符串的列表,例如['415-555-9999', '212-555-0000']
2.如果调用在一个有分组的正则表达式上,例如(\d\d\d)-(\d\d\d)-(\d\d\d\d),方法 findall()将返回一个字符串的元组的列表(每个分组对应一个字符串),例如[(‘415’, ‘555’, ‘1122’), (‘212’, ‘555’, ‘0000’)]。


5. 字符分类

在这里插入图片描述
在这里插入图片描述

>>> xmasRegex = re.compile(r'\d+\s\w+')
>>> xmasRegex.findall('12 drummers, 11 pipers, 10 lords, 9 ladies, 8 maids, 7
swans, 6 geese, 5 rings, 4 birds, 3 hens, 2 doves, 1 partridge') 
['12 drummers', '11 pipers', '10 lords', '9 ladies', '8 maids', '7 swans', '6
geese', '5 rings', '4 birds', '3 hens', '2 doves', '1 partridge']

正则表达式\d+\s\w+匹配的文本有一个或多个数字(\d+),接下来是一个空白字符(\s),接下来是一个或多个字母/数字/下划线字符(\w+)。


6. 建立自己的字符分类

字符分类[aeiouAEIOU]将匹配所有元音字符,不论大小写。在交互式环境中输入以下代码:

>>> vowelRegex = re.compile(r'[aeiouAEIOU]')
>>> vowelRegex.findall('RoboCop eats baby food. BABY FOOD.') 
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']

可以使用短横表示字母或数字的范围。例如,字符分类[a-zA-Z0-9]将匹配所有小写字母、大写字母和数字

通过在字符分类的左方括号后加上一个插入字符(^),就可以得到“非字符类”。非字符类将匹配不在这个字符类中的所有字符

>>> consonantRegex = re.compile(r'[^aeiouAEIOU]')
>>> consonantRegex.findall('RoboCop eats baby food. BABY FOOD.')
['R', 'b', 'c', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', '
', 'B', 'B', 'Y', ' ', 'F', 'D', '.'] 

现在,不是匹配所有元音字符,而是匹配所有非元音字符。


7. 插入字符和美元字符

可以在正则表达式的开始处使用插入符号(^),表明匹配必须发生在被查找文本开始处。类似地,可以在正则表达式的末尾加上美元符号($),表示该字符串必须以这个正则表达式的模式结束。

正则表达式 r'^Hello'匹配以’Hello’开始的字符串

>>> beginsWithHello = re.compile(r'^Hello') 
>>> beginsWithHello.search('Hello world!')
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
>>> beginsWithHello.search('He said hello.') == None
True

正则表达式 r'\d$'匹配以数字 0 到 9 结束的字符串

>>> endsWithNumber = re.compile(r'\d$')
>>> endsWithNumber.search('Your number is 42')
<_sre.SRE_Match object; span=(16, 17), match='2'>
>>> endsWithNumber.search('Your number is forty two.') == None
True

正则表达式 r'^\d+$'匹配从开始到结束都是数字的字符串


>>> wholeStringIsNum = re.compile(r'^\d+$') 
>>> wholeStringIsNum.search('1234567890')
<_sre.SRE_Match object; span=(0, 10), match='1234567890'>
>>> wholeStringIsNum.search('12345xyz67890') == None
True
>>> wholeStringIsNum.search('12 34567890') == None
True

1.在中括号[]里面的^表示非字符类:将匹配不在这个字符类中的所有字符
2.在单引号''里面的^表示匹配必须发生在被查找文本开始处 (以什么开始)


8. 通配字符

8.1 用点-星匹配所有字符

在正则表达式中,.(句点)字符称为“通配符”。它匹配除了换行之外的所有字符

>>> atRegex = re.compile(r'.at')
>>> atRegex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat'] 

可以用(.*)表示“任意文本”。因为句点字符表示“除换行外所有单个字符”,星号字符表示“前面字符出现零次或多次”

>>> nameRegex = re.compile(r'First Name: (.*) Last Name: (.*)')
>>> mo = nameRegex.search('First Name: Al Last Name: Sweigart')
>>> mo.group(1)
'Al'
>>> mo.group(2)
'Sweigart'

8.2 用句点字符匹配换行

.*将匹配除换行外的所有字符。通过传入 re.DOTALL 作为 re.compile()的二个参数,可以让句点字符匹配所有字符,包括换行字符:

>>> noNewlineRegex = re.compile('.*')
>>> noNewlineRegex.search('Serve the public trust.\nProtect the innocent. 
\nUphold the law.').group()
'Serve the public trust.'
>>> newlineRegex = re.compile('.*', re.DOTALL)
>>> newlineRegex.search('Serve the public trust.\nProtect the innocent.
\nUphold the law.').group()
'Serve the public trust.\nProtect the innocent.\nUphold the law.' 

正则表达式 noNewlineRegex 在创建时没有向 re.compile()传入 re.DOTALL,它将匹配所有字符,直到第一个换行字符。但是,newlineRegex 在创建时向 re.compile()传入了 re.DOTALL,它将匹配所有字符。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值