正则表达式
5.1 正则表达式概述
一、为什么要学习正则表达式
在实际开发过程中经常会有查找符合某些复杂规则的字符串的需要
比如:邮箱、手机号码、图片地址等
这时候想匹配或者查找符合某些规则的字符串就可以使用==正则表达式==了
二、什么是正则表达式
正则表达式(regular expression)是一种用于匹配字符串中字符组合的==模式==。它由普通字符和特殊字符(元字符)组成,形成了一种强大的文本处理工具。
模式:一种特定的字符串模式,这个模式是通过一些特殊的符号组成的。
正则表达式的特点:
正则表达式的语法很令人头疼,可读性差
正则表达式通用行很强,能够适用于很多编程语言
0\d{2}-\d{8} 这个就是一个正则表达式,表达的意思是匹配的是座机号码
正则表达式并不是Python所特有的,在Java、PHP、Go以及JavaScript等语言中都是支持正则表达式的。
三、正则表达式的功能
正则表达式可以用来检查一个大串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。
① 数据验证(表单验证、如手机、邮箱、IP地址) ② 数据检索(数据检索、数据抓取) ③ 数据隐藏(135****6235 王先生) ④ 数据过滤(敏感关键词过滤) …
5.2 正则表达式应用
一、正则表达式re模块
正则表达式单词: regular expression
python中的正则表达式模块: re
re 模块是 Python 内置的正则表达式处理模块,提供了强大的字符串匹配、查找、替换和分割功能。
re模块使用三大步:
1.导包
2.匹配数据
3.处理数据
二、正则表达式匹配方式
1 匹配方式概述
匹配方式1: re.match(正则表达式,'要匹配的大字符串',标志位)
`match特点: 只能从开头开始匹配,只能返回第一个匹配成功的内容
匹配成功: 返回匹配对象,还需要从匹配对象中使用group()提取数据
匹配失败: 返回None
`匹配方式2: re.search(正则表达式,'要匹配的大字符串',标志位)
`search特点: 可以整个字符串搜索,只能返回第一个匹配成功的内容
匹配成功: 返回匹配对象,还需要从匹配对象中使用group()提取数据
匹配失败: 返回None
`匹配方式3: re.findall(正则表达式,'要匹配的大字符串',标志位)
`findall特点: 可以整个字符串搜索,可以返回所有匹配成功的内容
匹配成功: 返回的包含所有匹配成功数据的列表->[元素1,元素2,元素3...]
匹配失败: 返回空列表->[]
`2 匹配方式练习
2.1 match匹配
`match匹配规则: 只从头开始匹配,只返回第一个匹配成功的数据
1.导包 : import re
2.匹配 : 匹配结果 = re.match(正则表达式,要匹配的大字符串内容)
3.处理数据: 如果匹配成功: 从返回的数据对象中把数据取出来 如果匹配失败: 返回None
``# 1.导包 : import re
import re
# 2.匹配 : 数据对象名 = re.match(正则表达式,要匹配的大字符串内容)
result = re.match('itheima', 'itheima.com')
# 3.处理数据: 从数据对象中把数据取出来
# 非空即为真(True)
if result:
print('匹配成功:', result.group())
else:
print('匹配失败!')
`2.2 search匹配
`search匹配规则: 在整个大字符串中查找,只返回第一个匹配成功的数据
1.导包 : import re
2.匹配 : 匹配结果 = re.search(正则表达式,要匹配的大字符串内容)
3.处理数据: 如果匹配成功: 从返回的数据对象中把数据取出来 如果匹配失败: 返回None
``# 1.导包 : import re
import re
# 2.匹配 : 数据对象名 = re.search(正则表达式,要匹配的大字符串内容)
result = re.search('itheima', 'www.itheima.com')
# 3.处理数据: 从数据对象中把数据取出来
# 非空即为真(True)
if result:
print('匹配成功:', result.group())
else:
print('匹配失败!')
`2.3 findall匹配
`search匹配规则: 在整个大字符串中查找,把所有匹配成功的数据放到列表中返回
1.导包 : import re
2.匹配 : 匹配结果 = re.findall(正则表达式,要匹配的大字符串内容)
3.处理数据: 如果匹配成功: 返回有内容的列表 如果匹配失败: 返回空列表
``# 1.导包 : import re
import re
# 2.匹配 : 数据对象名 = re.findall(正则表达式,要匹配的大字符串内容)
# 注意: findall匹配返回的是列表
result = re.findall('itheima', 'www.itheimaitheima.com')
# 3.处理数据: 打印列表
# 非空即为真(True)
if result:
print('匹配成功:',result)
else:
print('匹配失败!')
`三、正则表达式匹配规则
1 正则匹配单个字符规则
知识点:
`记忆方式: d:digit数字 s:space空白 w:word词
. : 任意1个字符(除了\n)
[]: 中括号中任意1个字符
\d: 任意1个数字(0-9)
\D: 非数字
\s: 任意1个空白(空格,\t,\n)
\S: 非空白
\w: 任意1个正常词(字母,数字,下划线,汉字)
\W: 非正常词(特殊字符)
`示例:
`import re
# 定义一个函数用于处理数据
def show(result):
if result:
print('匹配成功:', result.group())
else:
print('匹配失败!')
print('-----------------------------------------------------------')
# . : 任意1个字符(除了\n) 举例:binzi_666_帅气->.inzi.6.6_帅.
result = re.match('.inzi.6.6_帅.', 'binzi_666_帅气')
show(result)
result = re.match('binzi_666.帅气', 'binzi_666\n帅气')
show(result) # 匹配失败,因为.不能匹配\n
print('-----------------------------------------------------------')
# []: 中括号中任意1个字符 注意: 中括号里的内容是或者关系
result = re.match('[bB]inzi_666_帅气', 'binzi_666_帅气')
show(result)
result = re.match('[bB]inzi_[0-9][0-9][0-9]_帅气', 'binzi_888_帅气')
show(result)
print('-----------------------------------------------------------')
# \d: 任意1个数字(0-9) \d==[0-9]==[0123456789]
result = re.match('binzi_\d\d\d_帅气', 'binzi_888_帅气')
show(result)
print('-----------------------------------------------------------')
# \D: 非数字
result = re.match('binzi_\D\D\D_帅气', 'binzi_888_帅气')
show(result) # 匹配失败,因为\D代表非数字
result = re.match('\Dinzi\D888_帅\D', 'binzi_888_帅气')
show(result)
print('-----------------------------------------------------------')
# \s: 任意1个空白(空格,\t,\n)
result = re.match('binzi_666\s帅气', 'binzi_666 帅气')
show(result)
result = re.match('binzi_666\s帅气', 'binzi_666\t帅气')
show(result)
result = re.match('binzi_666\s帅气', 'binzi_666\n帅气')
show(result)
print('-----------------------------------------------------------')
# \S: 非空白
result = re.match('binzi_666\S帅气', 'binzi_666 帅气')
show(result) # 匹配失败,因为\S匹配非空白
result = re.match('\Sinzi\S6\S6 帅\S', 'binzi_666 帅气')
show(result)
print('-----------------------------------------------------------')
# \w: 任意1个正常词(字母,数字,下划线,汉字)
result = re.match('\winzi\w6\w6_帅\w', 'binzi_666_帅气')
show(result)
print('-----------------------------------------------------------')
# \W: 非正常词(特殊字符)
result = re.match('\Winzi\W6\W6_帅\W', 'binzi_666_帅气')
show(result) # 匹配失败,因为\W匹配特殊字符
result = re.match('binzi\W666\W帅气', 'binzi?666&帅气')
show(result)
`2 正则匹配多个字符规则
知识点:
`* : 匹配前一个字符出现0次或者无限次 次数>=0
+ : 匹配前一个字符出现1次或者无限次 次数>=1
? : 匹配前一个字符出现0次或者1次 次数== 0 或者 次数 == 1
{x}: 匹配前一个字符出现x次 次数==x
{x,y}:匹配前一个字符出现x到y次 次数>=x 并且 次数<=y
`示例:
`import re
# 定义一个函数用于处理数据
def show(result):
if result:
print('匹配成功:', result.group())
else:
print('匹配失败!')
# * : 匹配前一个字符出现0次或者无限次 次数>=0
result = re.match('[A-Z].*', 'Binzi666_帅气') # 需求:第一个字母大写,后面内容任意
show(result)
result = re.match('t.*o', 'to') # 需求: t和o中间可有可无任意字符
show(result)
result = re.match('张.*三', '张三') # 需求: 张和三中间可有可无任意字符(姓名个数>=2)
show(result)
print('----------------------------------------------')
# + : 匹配前一个字符出现1次或者无限次 次数>=1 需求: t和o中间至少一个任意字符
result = re.match('t.+o', 'to')
show(result) # 匹配失败,因为+要求至少1个
result = re.match('t.+o', 'tbo')
show(result)
result = re.match('t.+o', 'tbinzio')
show(result)
result = re.match('张.+三', '张三') # 需求: 张和三中间至少加一个字(姓名个数>=3)
show(result) # 匹配失败,因为+要求至少1个
print('----------------------------------------------')
# ? : 匹配前一个字符出现0次或者1次 次数== 0 或者 次数 == 1
result = re.match('https?', 'http')
show(result)
result = re.match('https?', 'https')
show(result)
print('----------------------------------------------')
# {x}: 匹配前一个字符出现x次 次数==x
result = re.match('binzi_[0-9]{3}_帅气', 'binzi_888_帅气')
show(result)
result = re.match('binzi_\d{3}_帅气', 'binzi_888_帅气')
show(result)
result = re.match('[a-zA-Z0-9_]{6}', 'abc_123') # 需求: 密码6位
show(result) # 注意此处有一个小的漏洞,一会儿使用匹配开头和结尾解决
print('----------------------------------------------')
# {x,y}:匹配前一个字符出现x到y次 次数>=x 并且 次数<=y
result = re.match('[a-zA-Z0-9_]{8,10}', 'abc_ABC_123_456') # 需求: 密码是8-10位
show(result) # 注意此处有一个小的漏洞,一会儿使用匹配开头和结尾解决
`3 正则匹配开头和结尾
知识点:
`^ : 匹配以什么开头
$ : 匹配以什么结尾
注意: ^和$一般都是配合使用,起到限制作用
`示例:
`import re
# 定义一个函数用于处理数据
def show(result):
if result:
print('匹配成功:', result.group())
else:
print('匹配失败!')
# 需求: 密码6位,多或者少都应该匹配失败
result = re.match('^[a-zA-Z0-9_]{6}$', 'abc_123')
show(result) # 匹配失败,说明^和$起到了限制作用
result = re.match('^[a-zA-Z0-9_]{6}$', 'abc123')
show(result)
# 需求: 密码是8-10位,多或者少都应该匹配失败
result = re.match('^[a-zA-Z0-9_]{8,10}$', 'abc_ABC_123_456')
show(result) # 匹配失败,说明^和$起到了限制作用
result = re.match('^[a-zA-Z0-9_]{8,10}$', 'abcABC123')
show(result)
`4 正则匹配分组
知识点:
`| : 匹配左右任意一个表达式 或者
(): 小括号中内容为一组,默认产生从1开始的编号
\n: 根据分组编号n引用对应的分组
(?P<name>) : 给分组起别名name
(?P=name) : 根据分组名name引用对应的分组
`示例:
`import re
# 定义一个函数,用于处理数据
def show(data):
if data:
print('匹配成功:', data.group())
else:
print('匹配失败!')
# | : 匹配左右任意一个表达式 或者
# (): 小括号中内容为一组,默认产生从1开始的编号
# 需求.匹配出163,qq,sina的邮箱地址,且@符号之前有4到20位,举例: hello@163.com
result = re.match('([a-zA-Z0-9_]{4,20})@(163|qq|sina)\.com', 'binzi@qq.com')
show(result)
print(result.group(0)) # 0拿的是所有匹配成功的数据
print(result.group(1)) # 根据分组编号1,拿到对应的分组
print(result.group(2)) # 根据分组编号2,拿到对应的分组
# \n: 根据分组编号n引用对应的分组
result = re.match('<([a-zA-Z1-6]+)>.*</\\1>', '<html>标签内容</html>')
show(result)
# (?P<name>) : 给分组起别名name
# (?P=name) : 根据分组名name引用对应的分组
result = re.match('<(?P<name1>[a-zA-Z1-6]+)>.*</(?P=name1)>', '<html>标签内容</html>')
show(result)
`5 特殊匹配
知识点:
`注意: 如果^放到了[]里面,表示取反作用
举例: [^\d] == [\D]
`示例:
`"""
注意: 如果^放到了[]里面,表示取反作用
举例: [^\d] == [\D] == \D
"""
# 定义一个函数用于处理数据
def show(result):
if result:
print('匹配成功:', result.group())
else:
print('匹配失败!')
# 1.导包
import re
# 2.1匹配
result = re.match('[\D].*', 'hello')
# 3.1处理数据
show(result) # 非数字开头,匹配成功
# 2.2匹配
result = re.match('[^\d].*', 'hello')
# 3.2处理数据
show(result) # 非数字开头,匹配成功
`四、综合练习
需求如下:
1.匹配出微博中的话题, 举例: #幸福是奋斗出来的#
2.匹配出11位手机号码, 举例: 18866668888
3.匹配出163的邮箱地址,且@符号之前有4到20位,举例: hello@163.com
参考示例:
`import re
# 定义一个函数,用于处理数据
def show(data):
if data:
print('匹配成功:', data.group())
else:
print('匹配失败!')
# 1.匹配出微博中的话题, 举例: #幸福是奋斗出来的#
result = re.match('#.+#', '#幸福是奋斗出来的#')
show(result)
print('-----------------------------------')
# 2.匹配出11位手机号码, 举例: 18866668888
result = re.match('^1[0-9]{10}$', '18866668888')
show(result)
print('-----------------------------------')
# 3.匹配出163的邮箱地址,且@符号之前有4到20位,举例: hello@163.com
result = re.match('^[a-zA-Z0-9_]{4,20}@163.com$', 'binzi@163,com')
show(result) # 应该报错,但是成功了,因为.有特殊含义: 除了\n的任意字符
# 知识点: 如果想要输出有特殊含义的字符,需要加转义符\,只代表本身含义
result = re.match('^[a-zA-Z0-9_]{4,20}@163\.com$', 'binzi@163,com')
show(result)
`五、贪婪模式和非贪婪模式
知识点:
`贪婪模式: 在整个表达式匹配成功的前提下,尽可能多的匹配
非贪婪模式: 在整个表达式匹配成功的前提下,尽可能少的匹配
正则中的量词:*和+,默认都是贪婪模式的匹配,可以在他们后面加?将其变为非贪婪模式。
常用贪婪模式如: .* .+
常用非贪婪模式如: .*? .+?
`示例:
`# 导包
import re
# 已知要匹配的字符串如下
my_str = '<h1>编程语言</h1>此处省略很多内容<h1>python大数据</h1>'
# .* : 贪婪模式,匹配成功的前提下,尽可能多的匹配
data = re.findall('<h1>.*</h1>', my_str)
print(data) # ['<h1>编程语言</h1>此处省略很多内容<h1>python大数据</h1>']
print(len(data)) # 1
# .*?: 非贪婪模式,匹配成功的前提下,尽可能少的匹配
data = re.findall('<h1>.*?</h1>', my_str)
print(data) # ['<h1>编程语言</h1>', '<h1>python大数据</h1>']
print(len(data)) # 2
# 需求: 只要标签中的标题名
# 知识点:findall匹配中如果有分组,那么只把对应分组里的内容放到列表中返回
data = re.findall('<h1>(.*?)</h1>', my_str)
print(data) # ['编程语言', 'python大数据']
print(len(data)) # 2
`六、正则修饰符
知识点:
`修饰符: 正则表达式可以包含一些可选标志修饰符来控制匹配的模式
常见的修饰符
re.I : 设置当前匹配不区分大小写
re.S : 设置.真正匹配到任意字符包含\n
`示例:
`# 导包
import re
# 演示不区分大小写的匹配:re.I
# 注意: 验证码必须绝对匹配,可以使用^和$配合起到限制作用
data = re.findall('^a8D9$', 'A8d9', re.I)
print(data) # ['A8d9']
# 演示.真正匹配到任意字符包含\n: re.S
print('------------------------')
my_str = '123\n456\n789'
print(my_str)
print('------------------------')
data = re.match('.{7}', my_str)
print(data) # None 因为.不能匹配到\n
print('------------------------')
data = re.match('.{7}', my_str, re.S)
print(data.group()) # 123\n456 因为re.S让.匹配到了\n
`