写这篇文档的目的是记录学习过程中已理解的正则表达式和对应代码实现,参考了很多资料,在文中就不一一进行列举。文章中相较其他的正则文档缺少许多内容,都是目前暂未理解的内容,等理解后再进行补充。
一、正则表达式(RE)概述
1、概述
正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE)),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。
正则表达式使用单个字符串来描述、匹配一系列匹配某个语法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
简而言之:如果遇到无法简单处理的字符串提取、替换问题,可以优先考虑正则表达式
2、优点
正则表达式可以进行多种文本匹配和替换操作,例如查找、替换、分割、提取等。正则表达式能够支持更复杂的模式匹配和替换操作。
3、缺点
正则表达式的语法相对复杂,需要学习正则表达式的规则和语法;正则表达式对于复杂的文本处理可能无法处理。
4、应用场景
正则表达式适用于需要进行高级模式匹配和替换的情况,例如:
(1)从文本中提取特定的信息。
(2)表单验证(手机号、邮箱、身份证)
(3)爬虫数据分析
二、RE在Python中实例
1、使用前需导入re模块
import re
2、match(pattern,string,flags)方法
(1)match()方法
描述:match()从字符串的开始处进行匹配
返回值:在字符串开头匹配到的对象,或者None
(2)参数
pattern:正则表达式
string:待匹配字符串
flags:正则表达式匹配方式,不写默认为flags=0。例如:是否区分大小写,多行匹配等
3、分组功能
所谓的分组就是去已经匹配到的内容再筛选出需要的内容,相当于二次过滤。实现分组靠(),而获取分组的内容靠的是group()、groups()。
4、RE简单演示
演示代码:
"""
示例一:
"""
# 导入re模块
import re
# 正则表达式
pattern = 'python'
# 待匹配字符串
s1 = 'python and java'
s2 = 'java and python'
print(f'pattern={pattern}\ns1={s1}\ns2={s2}')
# match()方法从字符串开头进行匹配,如果匹配不到返回None
print(f's1匹配结果:{re.match(pattern, s1)}')
print(f's2匹配结果:{re.match(pattern, s2)}')
# 利用group方法输出匹配到的数据
print(f'\n利用group方法输出匹配到的数据:{re.match(pattern, s1).group()}')
演示结果:
演示解析:
(1)我们用match方法通过pattern去匹配s1,s2的内容,获得s1匹配成功,s2匹配失败的结果。
(2)s2也有python这个内容,但匹配失败了。原因在于match方法只能从开头进行匹配,即开头不是python则匹配失败,返回None值
(3)s1匹配成功了,返回了re.match对象,匹配字符索引范围0-6,匹配到的内容为python
(4)如果想获取匹配的内容,对匹配成功数据调用group()功能,即可获取到python
三、元字符
1、概念
元字符用于匹配一些特殊含义和功能
2、元字符类别
表达式 | 匹配 |
. | 小数点可以匹配除了换行符\n以外的任意一个字符 |
| | 逻辑或操作符 |
[] | 匹配字符集中的一个字符 |
[^] | 对字符集求反。尖号必须在中括号最前面 |
- | 表示字符区间,例如[a-z] |
\ | 对后面的字符转义 |
() | 对表达式分组,获取匹配到的值 |
特殊含义的元字符
表达式 | 匹配 |
\r | 匹配回车 |
\n | 匹配换行 |
\t | 匹配制表符 |
\\ | 匹配\ |
\^ | 匹配^ |
\$ | 匹配$ |
\. | 匹配. |
3、元字符示例
需要学习哪个元字符,就把哪个注释取消
import re
# .
# print(re.match('a.c','abc').group())
# print(re.match('a.c','a你c').group())
# | 逻辑或操作符
# print(re.match('a|c','a').group())
# print(re.match('a|c','c').group())
# print(re.match('a|c','ac').group())
# print(re.match('a|c','ca').group())
# print(re.match('a|c','ba')) # 匹配失败,match与”b“匹配,返回None值
# print(re.match('a|c','ba').group()) # 会报错,None值不能调用group()功能
# []
# print(re.match('[abc]','a').group())
# print(re.match('[abc]','c').group())
# print(re.match('[abc]2','a2').group())
# # 匹配成功,"abc"中任意一个字符和2拼接,得到”a2“匹配上
# \
# print(re.match('速度与激情.','速度与激情.').group())
# print(re.match('速度与激情.','速度与激情9').group())
# print(re.match('速度与激情\.','速度与激情.').group())
# print(re.match('速度与激情\.','速度与激情9').group()) # 会报错,\.将.转义了
# -
# print(re.match('[a-z]','a').group())
# print(re.match('[A-Z]','A').group())
# print(re.match('[0-9]','3').group())
# ^
# print(re.match('[^a-z]','a').group()) # 报错
# print(re.match('[^a-z]','A').group()) # 匹配成功,A在[a-z]范围外,需参考ASCII值
# print(re.match('[^a-z]','1').group())
# () 分组功能
# print(re.match('(abc)', 'a').group()) # 报错,"abc"无法匹配"a"
# print(re.match('(abc)', 'abc').group())
# print(re.match('(ab.)', 'abr').group())
四、预定义匹配字符集
1、概念
用于匹配一类字符数据中的任意一个,例如\d可匹配任意一个数字字符。
2、预定义匹配字符集类别
表达式 | 匹配 |
\d | 0~9中任意一个数字 |
\w | A-Z,a-z,0-9,_(下划线)中的任意一个,常用于密码规则校验 |
\s | 空格、制表符、换页符中任意一个 |
\D | \d的反集,即非0~9的任意一个字符 |
\W | \w的反集 |
\S | \s的反集 |
3、预定义匹配字符集示例
import re
# \d 匹配任意一个数字
# print(re.match('a\dc','abc').group()) # 报错,数字无法匹配字符"b"
# print(re.match('a\dc','a1c').group())
# print(re.match('a\dc','a2c').group())
# \w
# print(re.match('a\wc','a2c').group())
# print(re.match('a\wc','aAc').group())
# print(re.match('a\wc','aBc').group())
# print(re.match('a\wc','a_c').group())
#\s
# print(re.match('a\sc','a c').group())
# print(re.match('a\sc','a\nc').group())
# print(re.match('a\sc','a\tc').group())
五、重复匹配
1、概念
因为预定义匹配字符集只能匹配一个内容,这样就会造成匹配多个字符时出现正则表达式书写繁杂的情况,例如想匹配11位手机号码13988888888,正则表达式需要书写11个\d\d\d\d\d\d\d\d\d\d\d的情况,但实际上只需要书写成\d{11}就可以了,这就是重复匹配。
2、重复匹配类别
表达式 | 匹配 |
{n} | 表达式重复n次。 \d{2}相当于\d\d,a{3}相当于aaa |
{m,n} | 表达式至少重复m次,最多重复n次。 ab{1,3},最少可以匹配ab,可以匹配abb,最多匹配abbb |
{m,} | 表达式至少匹配m次,至多匹配无限次。 \d{2,},至少匹配13,至多匹配889999999.... |
? | 表达式出现0次或者1次,相当于{0,1}。 a[cd]?可以匹配a,ac,ad |
+ | 表达式至少出现1次,相当于{1,} a+b可以匹配ab,aab,aaab ab+可以匹配ab,abb,abbb |
* | 表达式出现0次到任意次,相当于{0,} a*b可以匹配b,ab,aaaab ab*可以匹配a,ab,abbbb |
3、重复匹配示例
import re
# {n}
# print(re.match('\d','123').group()) # 报错,\d只能匹配一个数字字符
# print(re.match('\d\d\d','123').group())
# print(re.match('\d{3}','123').group())
#
# # {m,n}
# print(re.match('\d{3,4}-\d{7,8}','0730-1008611').group())
#
# # {m,}
# print(re.match('\d{3,}','123456789').group())
# * {0,}
# print(re.match('w[a-z]*','wa').group())
# print(re.match('w[a-z]*','wabc').group())
# print(re.match('w[a-z]*','w').group())
# + {1,}
# print(re.match('w[a-z]+','w').group()) # 报错,+至少匹配一个
# print(re.match('w[a-z]+','wabcd').group())
# print(re.match('w[a-z]+','wa').group())
# ? {0,1}
# print(re.match('w[cd]?','w').group())
# print(re.match('w[cd]?','wc').group())
# print(re.match('w[cd]?','wd').group())
# print(re.match('w[cd]?','wcd').group()) # 匹配成功,匹配到wc
# print(re.match('w[cd]?','wdc').group()) # 匹配成功,匹配到wd
六、位置匹配
1、概念
对开头、结尾、单词之间的位置进行匹配
2、位置匹配类别
表达式 | 匹配 |
^ | 在字符串开始的地方进行匹配,符号本身不匹配任何字符 |
$ | 在字符串结束的地方进行匹配,符号本身不匹配任何字符 |
3、位置匹配示例
import re
# 匹配开头
# print(re.match('^a\d{3,}','a123434565546').group()) # 匹配成功,必须以a开头
# 匹配结束
# print(re.match('^a\d{3,}b$','a123434565546b').group()) # 匹配成功,必须以a开头,b结尾
六、贪婪与非贪婪模式
在了解贪婪与非贪婪模式概念之前,我们要学一个YYDS的正则表达式:.*?,这个正则表达式的意思是匹配任意数量任意的字符一次,即匹配任意字符串。
import re
# 贪婪模式
s = '<div>abc</div><div>bcd</div>'
print(f'需匹配字符串{s}')
# 需求: 拿到 div>abc</div>
ptn = '<div>.*</div>' # .匹配任意一个数据 * 重复任意次
print(f'贪婪模式:{re.match(ptn,s).group()}')
# 非贪婪模式
ptn = '<div>.*?</div>' # .匹配任意一个数据 * 重复任意次 ? 0次到一次
print(f'非贪婪模式:{re.match(ptn,s).group()}')
匹配结果:
从匹配结果中可知.*?匹配了所有<div>XXX</div>中第一个结果,无论XXX这个字符串有多长,只要是第一个出现的<div>XXX</div>,都能匹配上。同理,若要文本中匹配两个<div>XXX</div>,只需写<div>.*?</div><div>.*?</div>。这样的话无论需要什么数据只要把需求数据的位置替换成.*?都能精确地匹配出来。这就是非贪婪模式。
在看看贪婪模式,正则表达式写法为.*,系统会尽可能把所有<div>XXX</div>类型的数据全部匹配出来,直至所有数据遍历完毕。这就是贪婪模式。
七、RE模块的匹配方法
1、概述
前面的示例中一直用match()方法对进行匹配,对它只能匹配开头的功能感到很头疼。所以它们来了,其他的正则匹配方法。
2、RE模块的匹配方法类别
方法 | 描述 | 返回值 |
compile(pattern,flags) | 根据包含正则表达式的字符串创建模式对象 | re对象 |
search(pattern,string,flags) | 在字符串中查找 | 第一个匹配到的对象或者Node |
match(pattern,string,flags) | 在字符串开始位置开始匹配 | 在字符串开始位置开始匹配的对象或者Node |
split(pattern,string,maxsplit,flags) | 根据模式的匹配项来分割字符串 | 分割后的字符串列表 |
findall(pattern,string,flags) | 列出字符串中模式的所有匹配项 | 所有匹配到的字符串列表 |
sub(pattern,replace,string,flags) | 将字符串中所有的pattern用replace替换 | 完成替换后的新字符串 |
3、RE模块的匹配方法示例
import re
# 参照匹配模式
# print(re.match('abc','abc').group())
# compile()
# 将正则表达式变成re对象,这样可直接调用其他方法,且无需填写正则表达式
# pat = re.compile('abc') # re对象
# print(pat.match('abc').group())
# search()
# print(re.search('abc','123abc123abc').group()) # 匹配成功
# split()
# s = '8*5+4-3/6'
# print(re.split('[/\\*\+\-]',s))
# findall()
# s = '8*5+4-3/6'
# print(re.findall('\d',s))
# sub()
# s2 = '我好好学学?'
# print(re.sub('[\/:*?"<>]','',s2)) # 将pattern中所有字符替换为' '
八、flages匹配模式
1、概述
方法中flages参数,相当于在正则表达式的基础上对匹配模式进行优化。
2、flages匹配模式类别
匹配模式 | 描述 |
re.I | 使匹配对大小写不敏感,也就是不区分大小写的模式 |
re.S | 使.这个通配符能够匹配包括换行在内的所有字符,针对多行匹配 |
3、flages匹配模式示例
import re
# re.I
print(re.match('a','A')) # 无法匹配,返回值为None
print(re.match('a','A',flags=re.I)) # 匹配成功,忽略大小写
# re.S
print(re.match('a.','a\n')) # 无法匹配,.无法匹配\n换行符
print(re.match('a.','a\n',flags=re.S)) # 匹配成功
九、分组功能
1、概念
所谓的分组就是去已经匹配到的内容再筛选出需要的内容,相当于二次过滤。实现分组靠(),而获取分组的内容靠的是group()、groups()。
group():默认返回整个匹配结果
group(参数):返回已匹配成功的指定位置的元素
groups(): 将匹配成功的数据以元组的形式返回
2、分组功能示例
import re
# 待匹配字符串
s = '<div>熊大</div>'\
'<div>熊二</div>'\
'<div>张三</div>' \
'<div>李四</div>' \
'<div>王五</div>' \
'<div>赵六</div>' \
'<div>田七</div>' \
'<div>周八</div>'
# 正则表达式:非贪婪模式
# () 对表达式进行分组,获取括号内的匹配值
ptn1 = '<div>.*?</div><div>.*?</div>'
ptn2 = '<div>(.*?)</div><div>(.*?)</div>'
# group() 默认返回整个匹配结果
print(f'group():{re.match(ptn1,s).group()}')
# group(参数) 返回指定位置的元素
print(f'group(1):{re.match(ptn2,s).group(1)}')
print(f'group(2):{re.match(ptn2,s).group(2)}')
# groups() 将匹配成功的数据以元组的形式返回
print(f'\ngroups():{re.match(ptn2,s).groups()}')
十、零宽断言
1、概念
零宽断言是正则表达式中的一种方法,正则表达式在计算机科学中,是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。
2、零宽断言类别
(1)正零宽断言:?=
3、零宽断言示例
import re # 导入正则表达式库
password = 'a1cdeAb.'
# 正则表达式
pattern = r'[a-z0-9]{8,}+' # 无效匹配模式
pattern1 = r'^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*,\.]).{8,}$' # 有效模式
pattern2 = r'^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*,\.])[0-9a-zA-Z!@#$%^&*,\\.]{8,}$' # 有效模式
"""
ab(?=A)可以匹配aabAcdw中的ab,但不能匹配或搜索除abcdabbc中的ab,表示:ab后必有A才能匹配成功
(?=.*[0-9]) 表示必有数字才能匹配成功
(?=.*[A-Z]) 表示必有大写字母才能匹配成功
(?=.*[a-z]) 表示必有小写字母才能匹配成功
(?=.*[!@#$%^&*,\.]) 表示必有这些符号"!@#$%^&*,\."才能匹配成功
零宽断言至少要有一个字符,否则匹配失败
[0-9a-zA-Z!@#$%^&*,\\.] 表示字符必须包含字母数字符号
{8,}表示至少八位
"""
print(re.match(pattern2, password))
十一、简单应用示例
(1)字符串匹配
需求:已知字符串'红米手机是小米旗下的手机品牌',要求统计'手机'字出现的次数
import re
string = '红米手机是小米旗下的手机品牌'
pattern = '手机'
# re.findall对全文进行匹配,返回匹配到的列表。
# re.match只能匹配开头,re.search匹配发现的第一个值
result = re.findall(pattern, string)
print(f'“{pattern}”总共出现了{len(result)}次')
(2)设置密码校验
需求:设置密码,要求密码为8位字母、数字、符号组合
"""
实例三:
设置密码,要求密码为8位字母、数字、符号组合
"""
import re # 导入正则表达式库
password = input('请输入密码,要求8位字母数字符号组合:')
#正则表达式
pattern = r'^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#$%^&*,\.])[0-9a-zA-Z!@#$%^&*,\\.]{8,}$'
result = re.match(pattern,password)
while result is None:
print('\n密码格式输入错误')
password = input('请重新输入密码,要求8位字母数字符号组合:')
result = re.match(pattern, password)
print('设置密码成功')