2.4字符串匹配和搜索
2.4字符串匹配和搜索
问题
你想匹配或者搜索特定模式的文本
解决方案
如果你想匹配的是字符串,那么你通常只需要调用基本字符串方法就行,比如str.find(),str.endswith(),str.startswith()或者类似的方法:
text = 'yeah,but no, but yeah, but no ,but yeah'
print(text == 'yeah')
print(text.startswith('yeah')) # ->True
print(text.endswith('no')) # ->False
print(text.find('no')) # ->9
对于复杂的匹配需要使用正则表达式和re模块,为了解释正则表达式的基本原理,假设你想匹配数字格式的字符串比如11/27/2021,你可以这样做
import re
if re.match(r'\d+/\d+/\d+',text1):
print('yes')
else:
print('no')
if re.match(r'\d+/\d+/\d+',text2):
print('yes')
else:
print('no')
如果你想使用同一个模式去做多次匹配你应该先将模式字符串预编译为模式对象。比如:
datepat=re.compile(r'\d+/\d+/|d+')
if datepat.match(text1):
print('yes')
else:
print('no')
if datepat.match(text2):
print('yes')
else:
print('no')
match()总是从字符串开头去匹配,如果比想查找字符串任意部分的模式出现位置,使用findall()方法去替代。比如:
text = 'Tody is 18/2/2022. PyCon starts 19/9/2018'
print(datepat.findall(text)) # ->['18/2/2022', '19/9/2018']
在定义正则表达式的时候,通常会利用括号去捕获分组。比如:
datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
m = datepat.match('18/02/2022')
print(m) # -><re.Match object; span=(0, 10), match='18/02/2022'>
print(m.group()) # ->18/02/2022
print(m.group(0)) # ->18/02/2022
print(m.group(1)) # ->18
print(m.group(2)) # ->02
print(m.group(3)) # ->2022
print(m.groups()) # ->('18', '02', '2022')
print(text) # ->Tody is 18/2/2022. PyCon starts 19/9/2018
print(datepat.findall(text))
for day, month, year in datepat.findall(text):
print(f"{year}-{month}-{day}")
'''
2022-2-18
2018-9-19
'''
findall()方法给搜索文件并以列表形式返回所有匹配,如果你想以迭代方式返回匹配,可以使用finditer()方法来替代,比如:
for m in datepat.finditer(text):
print(m.groups())
'''
('18', '2', '2022')
('19', '9', '2018')
'''
讨论
本节阐述了使用re模块的最简单进行匹配和搜索文本的最基本方法。核心步骤就是先使用re.compile()编译正则表达式字符串,然后使用match(),findall()或者finditer()等方法。
当写正则表达式字符串的时候,相对普遍的做法是使用原始字符串比如r'(\d+)/(\d+)/(\d+)'.这种字符串将不去解析反斜杠,这在正则表达式中是很有用的。如果不这样做的话,你必须使用两个反斜杠,类似'(\\d+)/(\\d+)/(\\d+)'。
需要注意的是match()方法仅仅检查字符串的开始部分。它的匹配结果又可能并不是你期望的那样。比如:
m=datepat.match('18/02/2022ABCDEF')
print(m)
print(m.group())# ->18/02/2022
如果你想精确匹配,确保你的正则表达式以$结尾,就像这样:
datepat = re.compile(r'(\d+)/(\d+)/(\d+)$')
print(datepat.match('18/02/2022')) # -><re.Match object; span=(0, 10), match='18/02/2022'>
print(datepat.match('18/02/2022abcdfe')) # ->None
最后,如果你仅仅是做了一次简单的文本匹配/搜索操作的话,可以略过编译部分,直接使用re模块级别的函数。比如:
print(re.findall(r'(\d+)/(\d+)/(\d+)',text)) # ->[('18', '2', '2022'), ('19', '9', '2018')]
但是需要注意 的是,如果你打算做大量的匹配和搜索抽操作的话,最好先编译正则表达式,然后再重复使用它。模块级别的函数将最近编译过的模式缓存起来,因此并不会消耗太多的性能,但是如果使用预编译模式的话,将会减少查找和一些额外的处理损耗。

浙公网安备 33010602011771号