文章目录
对近期Re库的学习进行一下复盘总结。
一、基本匹配步骤
最基本的正则表达式匹配模式有三种:1、匹配首个;2、匹配所有;3、匹配替换。
下面我们就一一介绍:
1、匹配首个
意思就是按照指定的正则表达式模式在字符串中寻找,如果找到了一个那么就立即返回并结束寻找。
使用到的核心方法:search()
匹配步骤:
- 导入库:Import re
- 创建正则表达式对象:Regex = re.compile()
- 查找字符串文本:mo = Regex.search()
- 返回查找对象:mo.group()
举个例子,在一串字符串文本中查找电话号码
import re # 导入库
phoneNumRegex = re.compile(r'\d{3}-\d{4}-\d{4}') # 创建正则表达式
mo = phoneNumRegex.search('My phone number is:185-1234-5678 and 139-8546-9952.') # 传入寻找字符串文本
print('Phone number:'+mo.group()) # 打印查找到的对象
我们可以运行一下看看结果,正如我们前面所说re.compile只会匹配首个,所以程序找到第一个电话号码就会停止。
2、匹配所有
与上述的search()方法不同,我们可以使用另一种方法查找指定字符串文本中所有符合正则表达式的字符串并返回。
它就是:findall()
匹配步骤:
- 导入库:Import re
- 创建正则表达式对象:Regex = re.compile()
- 查找字符串文本:mo = Regex.findall()
同样我们来举个例子
import re
phoneRegex = re.compile(r'\d{3}-\d{4}-\d{3}')
mo1 = phoneRegex.findall('My phone number is:185-1234-5678 and 139-8546-9952.')
print(mo1)
其代码运行结果为:
这里我们一定要注意区分search()与findall()之间的不同。
使用findall(),我们不需要通过调用group()来获得想要的内容,同时findall()返回的是所有符合要求的字符串的列表。
3、匹配替换
顾名思义,所谓的匹配替换,就是查找到我们所想要的内容并用另外的字符串进行替换。
这里我们要用到方法:sub()
匹配步骤:
- 导入库:Import re
- 创建正则表达式对象:Regex = re.compile()
- 查找并替换字符串:mo = Regex.sub(替换内容,原始字符,模式)
我们依旧用一个具体例子来看看
import re
namesRegex = re.compile(r'Agent \w+')
mo = namesRegex.sub('*****','Agent Alice gave the secret documents to Agent Bob')
print(mo)
让我们看看代码运行结果
我们就把对应的人名用‘“*****”进行了替换。
sub()的第三参数有0或1两种值,默认状态下是0代表全部替换,如果输入1则表示替换首个
import re
namesRegex = re.compile(r'Agent \w+')
mo = namesRegex.sub('*****','Agent Alice gave the secret documents to Agent Bob',1)
print(mo)
结果如下:
最后还需要提醒大家的是我们在构建正则表达式的时候最好使用原始字符串r
比如:phoneNumRegex = re.compile(r’\d{3}-\d{4}-\d{4}’)
——
二、匹配更多类型
接下来介绍一些强大武器让你的正则表达式更具有威力。
1、括号分组
什么是括号分组,我们通过一个实际的例子看一下
import re
phoneNumRegex = re.compile(r'(\d{3})-(\d{4}-\d{4})')
mo = phoneNumRegex.search('My phone number is:185-1234-5678 and 139-8546-9952.')
print('Phone number:'+mo.group())
print(mo.group(1))
print(mo.group(2))
print(mo.groups())
我们尤其要注意,此时与之前的区别在于正则表达式中有两个括号分别包裹起来的两个组:(\d{3}) 和 (\d{4}-\d{4})。
我们可以按照先后顺序,默认它们为1号、2号。
那么我们来看看打印的结果
由此可知:
- mo.group() 返回的完整的字符串
- mo.group(1) 返回组1号的字符串
- mo.group(2) 返回组2号的字符串
- mo.groups() 返回包含各组字符串的元组
2、管道or
首先我们得知道管道符号是这个:“|”。
它的功能也就相当于逻辑运算符中的“or”,让我们用实际的例子来看下。
import re
fruitRegex = re.compile(r'Apple|Banana')
mo1 = fruitRegex.search('My favorite fruits are Apple and Banana')
print(mo1.group())
mo2 = fruitRegex.search('My favorite fruits are Banana and Apple')
print(mo2.group())
分别看下两个的输出结果
正则表达式中的意义是我们要寻找的是Apple或Banana(其中一个),由于我们使用的是search(),所以先找到谁就返回谁。
3、问号0或1
通过使用问号:? ,我们可以实现可选匹配,也就是匹配0次或者1次。
import re
batRegex = re.compile(r'Bat(wo)?man')
mo1 = batRegex.search('The Adventures of Batman ')
print(mo1.group())
mo2 = batRegex.search('The Adventures of Batwoman ')
print(mo2.group())
mo3 = batRegex.search('The Adventures of Batwowowoman ')
print(mo3.group())
我们的正则表达式为:Bat(wo)?man,而我们的三个字符串中分别是:Batman、Batwoman、Batwowowoman。
正如我们说问号的功能是匹配0次或1次,最终我们的Batman和Batwoman被成功找到,但是最后的Batwowowoman出现了报错。
4、星号0或+
通过使用星号:* ,我们可以实现0次或多次匹配。
import re
batRegex = re.compile(r'Bat(wo)*man')
mo1 = batRegex.search('The Adventures of Batman')
print(mo1.group())
mo2 = batRegex.search('The Adventures of Batwoman')
print(mo2.group())
mo3 = batRegex.search('The Adventures of Batwowowowoman')
print(mo3.group())
我们使用同样的例子,但是这次我们的正则表达式为:Bat(wo)*man
最终我们匹配的结果是都寻找到了。
5、加号1或+
通过使用加号:+ ,我们可以实现1次或多次匹配,也就是至少出现一次。
import re
batRegex = re.compile(r'Bat(wo)+man')
mo1 = batRegex.search('The Adventures of Batwoman')
print(mo1.group())
mo2 = batRegex.search('The Adventures of Batwowowowoman')
print(mo2.group())
mo3 = batRegex.search('The Adventures of Batman')
print(mo3.group())
根据加号的功能我们应该能推断mo3是没能够匹配成功的。
6、花括号特定次数
通过使用花括号:{} ,并且通过设定上下限,我们可以指定任意最小和最大匹配次数。
import re
haRegex = re.compile(r'ha{3,5}')
mo1 = haRegex.search('hahahahaaa')
print(mo1.group())
mo2 = haRegex.search('hahahahaaaa')
print(mo2.group())
mo3 = haRegex.search('hahahahaaaaaa')
print(mo3.group())
例子中的意思是对a最少匹配3次,最多匹配5次,所以我们最终输出的结果是
7、贪心和非贪心
在上面花括号进行特定次数匹配的例子中,如果我们对字符串“hahahahaaaaaa”,希望能够匹配到“haaa”,这能够实现么?
答案是肯定的,这里就要涉及到贪心匹配和非贪心匹配了。
在默认情况下,程序都是按照贪心匹配模式执行(总是贪婪地匹配更多)。
但是如果我们在花括号后面加上一个问号就变成了非贪心匹配模式。
import re
haRegex1 = re.compile(r'ha{3,5}')
mo1 = haRegex1.search('hahahahaaaaaa')
print(mo1.group())
haRegex2 = re.compile(r'ha{3,5}?')
mo2 = haRegex2.search('hahahahaaaaaa')
print(mo2.group())
我们注意看结果
在这里我们要千万注意把非贪心模式的问号与匹配0次或1次的问号区分开来。
非贪心的问号一定是始终在花括号后面的。
8、边界匹配
最后我们来了解下边界匹配,也就是必须以我们指定的字符串开始或者结尾,它们分别是:^ 和 $ 。
我们同样拿具体例子说话。
import re
beginswithhello = re.compile(r'^Hello') # 以Hello开始
mo1 = beginswithhello.search('Hello world!')
endswithnum = re.compile(r'\d$') # 以数字结尾
mo2 = endswithnum.search('He has money 88')
print(mo2.group())
print(mo3.group())
mo3 = beginswithhello.search('You should say Hello')
print(mo3.group())
mo4 = endswithnum.search('He has money four')
print(mo4.group())
——
三、字符分类
1、常用自带元字符
如果你仔细地看了以上的案例,会发现 \d 代表了一个0-9之间的任意数字。也就是说,我们用一个 \d 就实现了正则表达式中(0|1|2|3|4|5|6|7|8|9)的功能,这样的字符就被称为元字符。
下面我们认识下正则表达式中最最最最常用的元字符:
- \d 0-9的任何数字
- \D 除0-9的数字以外的任何字符
- \w 任何字母、数字或下划线字符(匹配“单词”字符)
- \W 除字母、数字和下划线以外的任何字符
- \s 空格、制表符或换行符(匹配“空格”字符)
- \S 除空格、制表符或换行符以外的任何字符
一定要注意小写字母和大写字母之间的区别(其实就是互为对立)
import re
xmasRegex = re.compile(r'\d+\s\w+')
mo = xmasRegex.search('23faaff9 dfwef f2f29320vav091f f33jf29')
print(mo.group())
最终运行结果如下:
2、自定义元字符
百度搜索正则表达式元字符表,你能找到很多相应的元字符及其功能描述。
那么我们能不能按照自身的需求个性化自定义元字符呢?
Of course you can! 只需要使用方括号:[ ]
import re
vowelRegex = re.compile(r'[aeiouAEIOU]')
mo1 = vowelRegex.findall('RoboCop eat baby food. BABY FOODS')
print(mo1)
consonantRegex = re.compile(r'[^aeoiuAEOIU]')
mo2 = consonantRegex.findall('RoboCop eat baby food. BABY FOODS')
print(mo2)
r’[aeiouAEIOU]’ 代表了所有的元音字母;
在最前面加个 ^ ,r’[ ^aeiouAEIOU]’ 代表了所有非元音字母。
那么我们程序最终结果如下:
如果我们需要自定义的字符较多且连续,可以使用短横线来代表范围,比如 r’[a-zA-Z0-9]’ 代表了所有字母和数字。
——
四、通配符
1、认识句点
有没有这样的一种元字符能够涵盖所有的字母、数字以及其他各种各样的符号。
那就是通配符:.(句点)
要注意的是:
- 一个通配符匹配一个字符
- 通配符也并非万能,可匹配除了换行符之外的所有字符
2、点-星匹配
重点学会 点-星符号 的组合使用,这里放上例子仔细体会下,一个是贪心匹配,另一个是非贪心匹配。
import re
nongreedyRegex1 = re.compile(r'<.*>')
mo1 = nongreedyRegex1.search('<To serve man>for dinner>')
print(mo1.group())
nongreedyRegex2 = re.compile(r'<.*?>')
mo2 = nongreedyRegex2.search('<To serve man>for dinner>')
print(mo2.group())
3、匹配换行符
上面我们说到通配符并非万能,因为还有换行符是它所不能匹配的。但是我们也可以有方法能够让通配符能够匹配换行符,只需要在使用re.compile()构建正则表达式的时候传入第二参数:re.DOTALL 或者 re.S。
import re
nonewlineRegex = re.compile(r'.*')
mo1 = nonewlineRegex.search('Serve the public trust.\nProtect the innocent.\nUphpld the law.')
print(mo1.group())
print('------------------------------------------')
newlineRegex = re.compile(r'.*',re.DOTALL)
mo2 = newlineRegex.search('Serve the public trust.\nProtect the innocent.\nUphpld the law.')
print(mo2.group())
其中 \n 代表的是换行符,从mo1中我们可以看到正则表达式只是匹配到 “Serve the public trust.”;而mo2则是将换行符也匹配到了。
如果你觉得re.DOTALL太长了,可以使用re.S,同样可以使得通配符具有匹配换行符的功能。
——
五、匹配更多模式
通过上面通配符的例子,我们知道了re.compile()方法中不仅能够传入正则表达式,还能传入第二参数来匹配更多模式。
那么我们最后重点认识下经常会用到的第二参数有哪些。
1、re.DOTALL/re.S 匹配换行符
re.compile(正则表达式,re.DOTALL),从而使得通配符具有可匹配换行符的工具,真正成为匹配一切的符号。
已经讲过,这里不再重复。
2、re.I 不区分大小写
我们在构建自定义元字符的时候,如果想让表达式同时匹配大小写字母,那么得写成 r’[a-zA-Z]’ 。
如果传入了第二参数 re.I,那么表达式就能够实现同时匹配大小写字母的功能。
import re
robocop = re.compile(r'robocop',re.I)
mo1 = robocop.search('Robocop is part man, part machine,all cop.')
mo2 = robocop.search('robocop is part man, part machine,all cop.')
mo3 = robocop.search('ROBOCOP is part man, part machine,all cop.')
print(mo1.group())
print(mo2.group())
print(mo3.group())
3、re.VERBOSE 管理复杂表达式
随着我们需求的深入,我们需要写更加复杂冗长的正则表达式。
为了能够方便自己、方便别人阅读理解表达式,我们可能需要对其进行分行并添加注释。
这个时候我们就需要传入 re.VERBOSE,从而实现复杂正则表达式的管理。
比如下面匹配电话号码的表达式,有些电话号码有区号或没有区号或区号用了()进行包裹;有些电话号码有分隔符或没有分隔符,分隔符有空格、短横线、句点等类别;有些电话号码最后可能有附加信息等等。
为了便于理解和书写,我们就使用了re.VERBOSE参数,从而使得表达式可以换行并添加注释。
需要注意的是,这个时候我们需要使用三引号。
import re
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)
phone1 = phoneRegex.search("The company's phonenumber:434-567-5433 ext 323")
print(phone1.group())
phone2 = phoneRegex.search("The company's phonenumber:(023) 567-5433")
print(phone2.group())
4、利用管道组合使用
第二参数的位置只有一个,如果我们想同时以上多种模式,该怎么办。
我们可以用之前学的管道符号来实现多模式的组合使用。
re.compile(r’[.*a-z]’,re.I | re.DOTALL | re.VERBOSE)
——