day6 — 正则表达式
1.正则表达式
# (1).什么是正则
根据指定规则从字符串中提取子字符串
指定规则: 正则表达式
字符串: 待匹配字符串
子字符串: 匹配结果
注意: 正则并不是python独有的, 各种语言中均可以使用正则
# (2).正则表达式应用场景
1.表单验证(手机号验证, 账号验证, 密码验证)
2.爬虫
3.其他
2.正则表达式语法
# 1.元字符
(1) .(点): 匹配任意字符, 换行符除外\n ****
(2) []: 字符组, 一些特殊的字符组如下: ****
[a-z]: a到z的任意小写字母
[A-Z]: A到Z的任意大写字母
[0-9]: 0到9的任意数字
(3) \d: 匹配任意数字 *****
(4) \D: 匹配非数字
(5) \s: 匹配空白符
(6) \S: 匹配非空白符
(7) \w: 匹配数字字母下划线 ****
(8) \W: 匹配非数字字母下划线
(9) ^: 以什么开头 *****
(10) $: 以什么结尾 *****
# 小练习:
1.用户输入一个字符串, 这个字符串中可能包含abc?def类型的子字符串, 先需要匹配出字符串中的abc, def及其之间的那个字符, 如skfjabc9deflskjdf
- 正则表达式: abc.def
- 解释: 因为确定abc与def之间只有一个字符, 所以abc和def直接写死就好了, 那他们之间的那个字符是什么不确定, 所以用.来代表可以是任意一个字符.
- 思考: 如果不确定abc与def之间到底有几个字符怎么办, 有可能是1个, 有可能是多个, 还有可能没有.....
2.接收用户输入, 使用正则匹配出字符串中的所有数字, 如abd23kjsd9 --> 2, 3, 9
- 正则表达式: [0-9] or \d
- 解释: []代表字符组, 0-9代表0到9之间的任意数字, 也可以用\d代表任意数字
- 思考: 如果我不想让23匹配出来是分开的怎么办呢, 比如我要计算字符串中数的和, 上面的例子要求计算的是23+9, 但是如果使用上面的正则表达式匹配出来的是2, 3, 9, 则计算的是2+3+9.
# 2.量词: 匹配字符的数量
*: 匹配0个或无数个
+: 匹配1次过无数个
?: 匹配1个或0个
{m}: 匹配m个
{m,n}: 匹配n个到m个
{m,}: 匹配至少m个
{,n}: 匹配至多n个
# 遗留问题解决:
1.用户输入一个字符串, 这个字符串中可能包含abc???...def类型的子字符串, 先需要匹配出字符串中的abc, def及其之间的哪个字符
正则表达式: abc.*def
解释: abc和def是确定的, 中间用.代表匹配任意字符, *代表可以没有字符, 也可以有多个字符
问题: 如果用上面的正则表达式匹配: ksabc89def-78abc666def, 结果还尽如人意吗?
2.接收用户输入, 使用正则匹配出字符串中的所有数字, 如abd23kjsd9
正则表达式: \d+
解释: \d代表任意数字, +代表匹配1个或多个
# 3.贪婪匹配与非贪婪匹配
(1).贪婪匹配是指在正则匹配的过程中尽量往多了匹配, 正则表达式默认是贪婪匹配.
如上面的例子: ksabc89def-78abc666def字符串使用abc.*def进行匹配, 根据正则表达式第一次会从ks后边的abc考试向后匹配, 遇到89后面的def起始已经满足条件了, 但正则是贪婪的, 他先匹配跟多的字符出来, 于是继续向后找, 直到找到最后的def, 他发现从ks后面的abc开始, 一直到最后的def之间有很多字符, 并且之间的字符满足.*的匹配规则, 所以他就都给匹配出来了
(2).非贪婪匹配是指一但满足正则表达式,就将匹配到的作为一个结果, 然后继续向后匹配, 在匹配到一段满足正则表达式的子字符串又作为一个结果, 直到找不出满足正则表达式的结果为止. 换言之, 就是一个子字符串尽量往少了匹配.
非贪婪匹配需要在量词后面使用?进行修饰
如上面的例子: ksabc89def-78abc666def使用abc.*?def进行匹配即可得到'abc89def'和'abc666def'两个结果
# 4.分组与或
# 分组:
(1).分组: 是在正则表达式中使用()将正则表达式的一部分括起来表示一部分
(2).作用:
改变优先级 *****
分组引用
分组捕获
# 或的使用: 用"|"分隔的正则表达式, 代表按照|两边的正则表达式进行匹配都行
(1).使用的符号: | (管道符)
(2).示例:
abc(\d+|def)
解释: 正则表达式前面的abc是固定的, 必须匹配abc三个字母, abc后面是1个或多个数字可以作为匹配结果, abc后面是def三个字母也满足正则表达式, 比如按照上面的正则表达式对"abc789---abcdef999"的匹配结果为abc789和abcdef
**做一做: **
1.利用正则匹配出字符串中的所有数, 比如s = 'abc123kdfj789', 匹配结果为123, 789
2.利用正则匹配出以a开头, 以z结尾的字符串
3.匹配出字符串s = 'jjabckkksdjfabc89kkk'中abc???...kkk形式的字符串, abc与kkk之间可能有任意多个任意字符
4.匹配字符串中的所有合法手机号, 手机号共11位数字, 第一位以1开头, 第二位数为的数字在3-8之间, 其余为9个任意数字
s = 'kdjfs你好13724265588克里斯京东方17679962330kjsldf11324567982'
5.提取出下面HTML代码中的所有图片的src连接
html = '''
<html>
<head>
<title>
美女
<title>
</head>
<body>
<img src="//img.ivsky.com/img/bizhi/li/201911/20/yinger-004.jpg" alt="美女明星颖儿桌面壁纸">
<img src="//img.ivsky.com/img/bizhi/li/201911/18/fujing-002.jpg" alt="美女歌手傅菁桌面壁纸">
<img src="//img.ivsky.com/img/bizhi/li/201910/30/yangmi-006.jpg" alt="美女明星杨幂桌面壁纸">
<img src="//img.ivsky.com/img/bizhi/li/201910/30/nini.jpg" alt="美女明星倪妮桌面壁纸">
</body>
</html>
'''
3.re模块
# re模块是做什么的
re模块是python提供正则的接口模块, 让你能够在python程序中使用正则
# 重点掌握以下方法:
1.re.findall()
2.re.match()
3.re.search()
4.re.compile()
3.1 findall方法
# re.findall(r'expression', str): 扫描整个字符串, 以列表形式返回所有匹配到的结果
import re
s = 'k789lfsdkjf123sldkjfoe666'
ret = re.findall(r'\d+', s)
print(ret)
# 拓:分组的妙使用
s = '<img src="https://enterdesk.com/kjsdlfj/soiejfn/2020/6/21/jlsjfdls.jpg"></img>'
ret = re.findall(r'src="(.*?)"', s)
print(ret)
3.2 match方法
# re.match(r'expression', str): 从字符串开头进行匹配, 如果开头就不符合则匹配不到结果返回None, 如果匹配到结果后返回一个对象, 使用group进行取值
import re
s1 = 'a789lfsdkjfa123sldkjfoa666'
ret1 = re.match(r'a\d+', s)
print(ret1)
print(ret1.group())
s2 = 'a789lfsdkjfa123sldkjfoa666'
ret2 = re.match(r'a\d+', s)
print(ret2)
print(ret2.group())
# 注意: 在使用match进行正则匹配时, 不可对匹配结果直接使用group()进行取值, 因为group()是匹配对象的方法, 但在程序中并不能确定一会能够匹配到结果返回一个对象, 当匹配不到结果的时候会返回None, 而None不是匹配结果对象. 所以None没有group()方法, 直接对匹配结果使用group()可能会报错.
3.3 search()方法
# re.search(r'expression', str): 从头开始扫描字符串, 匹配到一个结果就立即返回该结果对象, 使用group()进行取值, 如果匹配不到结果则返回None.
import re
s = 'a789lfsdkjfa123sldkjfoa666'
ret = re.search(r'\d+', s)
print(ret)
print(ret.group())
3.4 compile方法
# re.compile(r'expression'): 用于编译正则表达式, 便于后续使用
# 示例:
import re
pattern = re.compile(r'\d+')
ret = pattern.findall("jslkdfj789ksjdf")
print(ret)
# 你的疑问: 为啥要脱掉裤子排出废气?
1.compile可以编译正则表达式, 对于复杂的表达式编译一次即可在后续使用
2.经过编译的正则表达式, 在匹配是效率更高
# 实测差别不大(测试不太严谨, 哈哈哈)
# 利用装饰器测试使用compile和不适用compile进行正则匹配的效率
...
拓展:
# 回溯:
1.一个细致的匹配过程
对字符串"abcsdfsdfsdf1234"使用"abc\w+\d{3}"进行正则匹配
2.回溯概念:
往回退的操作就是回溯
# 优化:
1、使用正确的边界匹配器(^、$等),限定搜索字符串位置
2、使用具体的元字符、字符类(\d、\w、\s等) ,少用”.”字符
3、使用正确的量词(+、*、?、{n,m}),如果能够限定长度,匹配最佳