正则表达式(字符串的匹配规则)
在实际开发过程中经常会有查找符合某些复杂规则的字符串的需要
比如:邮箱、图片地址、手机号码等
这时候想匹配或者查找符合某些规则的字符串就可以使用正则表达式了
什么是正则表达式
正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。
模式:一种特定的字符串模式,这个模式是通过一些特殊的符号组成的。
 某种:也可以理解为是一种模糊匹配。
精准匹配:select * from blog where title=‘python’;
模糊匹配:select * from blog where title like ‘%python%’;
正则表达式并不是Python所特有的,在Java、PHP、Go以及JavaScript等语言中都是支持正则表达式的。
正则表达式的用途
① 数据验证(表单验证、如手机、邮箱、IP地址)
 ② 数据检索(数据检索、数据抓取)
 ③ 数据隐藏(100****0086 小黑子)
 ④ 数据过滤(论坛敏感关键词过滤)
re模块 for 正则表达式
什么是re模块
在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用一个re模块(regexp:正则模块)
re模块使用方法
三步
# 第一步:导入re模块
import re
# 第二步:使用match方法进行匹配操作
# match方法只能匹配开头,如果要匹配任意位置可以用findall(),findall返回的是一个列表
result = re.match(pattern正则表达式, string要匹配的字符串, flags=0)
# 第三步:如果数据匹配成功,使用group方法来提取数据
result.group()
 
re模块的相关方法
- re.match(pattern, string, flags=0) 
- 匹配字符串开头的第一条符合条件的结果。从字符串的起始位置匹配,如果匹配成功则返回匹配内容(返回re正则对象,需要通过
result.group()获取内容), 否则返回None 
 - 匹配字符串开头的第一条符合条件的结果。从字符串的起始位置匹配,如果匹配成功则返回匹配内容(返回re正则对象,需要通过
 
- re.search() 
- 匹配满足正则表达式的结果,没有位置限制,在哪都可以,但是只匹配第一个满足条件的结果(返回re正则对象,需要通过
result.group(分组编号)获取内容) 
 - 匹配满足正则表达式的结果,没有位置限制,在哪都可以,但是只匹配第一个满足条件的结果(返回re正则对象,需要通过
 
- re.findall(pattern, string, flags=0) 
- 扫描整个串,返回所有与pattern匹配的列表
 - 注意: 如果pattern中有分组则返回与分组匹配的列表(无法获取分组中的内容)
 - 举例: 
re.findall("\d","chuan1zhi2") >> ["1","2"] 
 - re.finditer(pattern, string, flags) 
- 功能与上面findall一样,不过返回的是迭代器
 - 有满足正则表达式的结果(所有)没有位置限制,finditer()不仅可以匹配到整个正则匹配到的结果,其还可以专门用于获取分组中得到的数据,可以使用
result.group(分组编号)获取分组内容 
 - re.split() 
- 按照指定字符对字符串进行分割操作,也支持正则表达式,返回列表
 - 举例: 
re.split("\d","chuan1zhi2") >> ["chu", "zhi"],解释:按照数字对字符串进行分割,返回列表 
 - re.sub() 
- 替换字符串中所有匹配正则表达式的字符串,返回替换后的字符串
 - 举例: 
re.sub("\d","*","chuan1zhi2") >> "chuan*zhi*",解释:将字符串中的数字替换为* 
 - re.compile() 
- 将正则表达式编译成正则表达式对象,用于提高效率
 - 举例: 
re.compile("\d"),解释:将正则表达式编译成正则表达式对象 
 
正则表达式基础
正则编写三步走:查什么、查多少、从哪查
正则表达式通常是由两部分数据组成的:普通字符 与 元字符
普通字符:0123456789abcd@...
元字符:正则表达式所特有的符号 => [0-9],^,*,+,?
1、查什么
| 代码 | 功能 | 
|---|---|
. | 匹配任意某1个字符(除了\n)(万能字符) | 
[ ] | 匹配[ ]中列举的某1个字符,专业名词 => 字符簇 | 
[^指定字符] | 匹配除了指定字符以外的其他某个字符(取反),^专业名词 => 托字节 | 
\d | 匹配数字,即0-9 | 
\D | 匹配非数字,即不是数字 | 
\s | 匹配空白,即 空格,tab键 | 
\S | 匹配非空白 | 
\w | 匹配非特殊字符,即a-z、A-Z、0-9、_ | 
\W | 匹配特殊字符,即非字母、非数字、非下划线 | 
字符簇常见写法:
 ① [abcdefg] 代表匹配abcdefg字符中的任意某个字符(1个)
 ② [aeiou] 代表匹配a、e、i、o、u五个字符中的任意某个字符
 ③ [a-z] 代表匹配a-z之间26个字符中的任意某个
 ④ [A-Z] 代表匹配A-Z之间26个字符中的任意某个
 ⑤ [0-9] 代表匹配0-9之间10个字符中的任意某个
 ⑥ [0-9a-zA-Z] 代表匹配0-9之间、a-z之间、A-Z之间的任意某个字符
例如,匹配第一个数字:
str = '22afff3f4g'
result = re.match('[0-9]', str)
print(result.group())
 
字符簇 + 托字节结合代表取反的含义:
 ① [^aeiou] 代表匹配除了a、e、i、o、u以外的任意某个字符
 ② [^a-z] 代表匹配除了a-z以外的任意某个字符
\d 等价于 [0-9], 代表匹配0-9之间的任意数字
 \D 等价于 [^0-9],代表匹配非数字字符,只能匹配1个
str = '22afff3f4g'
result = re.match('\d', str)
print(result.group())
 
str = 'czc0927@XXGG.com$'
result = re.match('\W', str)
print(result.group())
 
这个代码能够匹配出’$'这个特殊字符
2、查多少
| 代码 | 功能 | 
|---|---|
| * | 匹配前一个字符出现任意次:src="(.*)":匹配src="任意字符"任意次 | 
| + | 匹配前一个字符出现至少1次:\w+ :常规字符出现至少一次 | 
| ? | 匹配前一个字符出现1次或者0次:10?:匹配10或者1 | 
| {m} | 匹配前一个字符出现m次;\d{11}:匹配11位手机号码 | 
| {m,} | 匹配前一个字符至少出现m次,\w{3,},代表前面这个字符最少要出现3次,最多可以是无限次 | 
| {m,n} | 匹配前一个字符出现从m到n次,\w{6,10},代表前面这个字符出现6到10次 | 
基本语法:
 正则匹配字符.或\w或\S + 跟查多少
 如\w{6, 10}
 如.*,匹配前面的字符出现0次或多次
实际栗子1:
匹配连续的三个数字:
str = '123aaa283Ikun'
result = re.match('\d{3}', str)
print(result.group())
 
控制台输出:
123
 
实际栗子2:
匹配多个字符:
str = '123aaa283Ikun.txt'
result = re.match('.*', str)  # 匹配任意字符出现0次或多次
print(result.group())
 
控制台输出:
123aaa283Ikun.txt
 
实际栗子3:
匹配手机号码是否合理:
str = '13812345678'
result = re.match('1[3-9]\d{9}', str)
print(result.group())
if result:print('匹配成功')
else:print('匹配失败')
 
这个代码存在问题,当电话号码是13812345678时,会匹配成功,但是当电话号码是1381234567890时,也会匹配成功,这显然是不合理的。所以需要使用^和$(从哪查)来限制电话号码的长度。
3、从哪查
默认都是从头开始查,现在指定从哪开始查,查到哪里结束
| 代码 | 功能 | 
|---|---|
| ^ | 匹配以某个字符串开头 | 
| $ | 匹配以某个字符串结尾 | 
栗子:
 匹配手机号码完善版
str = '13812345678'
result = re.match('^1[3-9]\d{9}$', str)  # 添加限制只能匹配11位手机号码
print(result.group())
if result:print('匹配成功')
else:print('匹配失败')
 
正则表达式高级
子表达式(分组)
在正则表达式中,通过一对圆括号括起来的内容,我们就称之为==“子表达式”==。
re.search(r'\d(\d)(\d)', 'abcdef123ghijklmn')注意:Python正则表达式前的 r 表示原生字符串(rawstring),该字符串声明了引号中的内容表示该内容的原始含义,避免了多次转义造成的反斜杠困扰。
 
正则表达式中\d\d\d中,(\d)(\d)就是子表达式,一共有两个()圆括号,则代表两个子表达式
说明:findall方法,如果pattern中有分组则返回与分组匹配的列表,所以分组操作中不适合使用findall方法,建议使用search(匹配一个)或finditer(匹配多个)方法。
捕获
当正则表达式在字符串中匹配到相应的内容后,计算机系统会自动把子表达式所匹配的到内容放入到系统的对应缓存区中
栗子:
import re
# 匹配字符串中连续出现的两个相同的单词
str1 = 'abcdef123ghijklmn'
result = re.search(r'\d(\d)(\d)', str1)
print(result.group())  # 123(获取正则匹配到的所有内容)
print(result.group(1))  # 2(获取1号分组匹配到的内容)
print(result.group(2))  # 3(获取2号分组匹配到的内容)
 
分组引用 反向引用(后向引用)
在正则表达式中,我们可以通过\n(n代表第n个缓存区的编号)来引用缓存区中的内容,我们把这个过程就称之为"反向引用"。
① 连续4个数字
re.search(r’\d\d\d\d, str1)
1234、5678、6789
② 连续的4个数字,但是数字的格式为1111、2222、3333、4444、5555效果?
re.search(r'(\d)\1\1\1, str1)
- 这里的r代表吧
\反斜杠转义字符当作普通的字符处理 - 这里的
\1代表第一组,也就是第一个括号的分组 
选择匹配
正则表达式中可以使用|匹配多个规则
案例:匹配字符串hellojava或hellopython
import re
str = 'hellojava, hellopython'
result = re.finditer(r'hello(java|python)', str)  # 这里用finditer方法
if result:for i in result:print(i.group())
else:print('未匹配到任何数据')
 
分组别名
把正则表达式中的分组起一个别名,方便后期查找和使用
| 代码 | 功能 | 
|---|---|
| (?P) | 分组起别名 | 
| (?P=name) | 引用别名为name分组匹配到的字符串 | 
| 主要用于HTML标签的匹配 | 
案例:匹配 <book></book>
# 导入模块
import restr1 = '<book></book>'
result = re.search(r'<(?P<mark>\w+)></(?P=mark)>', str1)print(result.group())
 
?P<mark>:给正则表达式起了一个别名,叫mark
 ?P=mark:引用刚才定义的mark别名的正则表达式
扩展:使用正则工具箱
复杂的正则表达式很难写,用工具箱来代写
直接搜索引擎搜索就有很多
- 工具箱
 - 常用表达式,带有python代码的
 
或者用AI来写
综合案例
①需求:在列表中[“apple”, “banana”, “orange”, “pear”],匹配apple和pear
import relist1 = ["apple", "banana", "orange", "pear"]
# 吧列表转为字符串
str1 = str(list1)
# 使用正则表达式匹配apple和pear
result = re.finditer('(apple|pear)', str1)
if result:for i in result:print(i.group())
else:print('未匹配到任何数据')
 
② 需求:匹配出163、126、qq等邮箱
 转义字符:如果我们想把某个元字符转换为普通字符,可以使用\反斜杠实现
import reemail = '1478670@qq.com, go@126.com, heima123@163.com'
result = re.finditer(r'\w+@(qq|126|163)\.com', email)
if result:for i in result:print(i.group())
else:print('未匹配到任何数据')
 
③需求 : 匹配qq:10567这样的数据,提取出来qq文字和qq号码
import restr1 = 'qq:913809'
result = re.split(r':', str1)
if result:print(f'{result[0]}号:{result[1]}')
else:print('未匹配到任何数据')
 
④需求:匹配出<html>hh</html>
import restr1 = '<html>hh</html>'
result = re.search(r'<(?P<mark>\w+)>(?P=mark)</(?P=mark)>', str1)
print(result.group())
 
⑤需求:匹配出<html><h1>www.baidu.com</h1></html>
import restr1 = '<html><h1>www.baidu.com</h1></html>'
result = re.match(r'<(\w+)><(\w+)>(.*)</\2></\1>', str1)
# 使用分组别名
# result = re.search(r'<(?P<mark>\w+)><(?P<mark2>\w+)>(?P=mark2)</(?P=mark)>', str1)
# print(result.group())
print(result.group(3))  # 获取第三个分组匹配到的内容:www.baidu.com
