目录
2.3 re.sub('匹配正则' , '替换内容' , 'string')
1、前言
1.1 什么是正则表达式
正则表达式,又称规则表达式,(Regular Expression,在代码中常简写为regex、regexp或RE),是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式(规则)的文本。
1.2 正则的目的
- 数据挖掘
- 验证
- 指定一个匹配规则,从而识别该规则是否在一个更大的文本字符串中
- 正则表达式可以识别匹配规则的文本是否存在
- 还能将一个规则分解为一个或多个子规则,并展示每个子规则匹配的文本
1.3 正则表达式的优缺点
- 优点:提高工作效率、节省代码
- 缺点:复杂,难于理解
官方文档:re — Regular expression operations — Python 3.10.6 documentation
1.4 符号
- ? 匹配前一项0次或一次
- . 匹配任意一个除换行符以外的字符,占位符
- + 匹配前一项一次以上 1~n次
- * 匹配前一项任意次 0~n次
- {n,m} 匹配前一项n~m次
- {n,} 匹配前一项n次以上
- {,m} 匹配前一项0~m次
- {n} 匹配前一项n次
- ^ 开头
- $ 结尾
- \Axx 匹配以xx字符串开始
- \b 匹配词边界(字母数字中文都属于单词字符)
- \B 匹配非词边界
- \w 匹配单词字符
- \W 匹配非单词字符
- \d 匹配数字
- \D 匹配非数字
- \s 匹配空白
- \S 匹配非空白
2、re模块的基本用法
2.1 match与search 查找第一个匹配
2.1.1 re.search
查找匹配项,接受一个正则表达式和字符串,并返回发现的第一个匹配。如果完全没有找到匹配,re.search返回None。
import re
rest = re.search(r"san","hello san")
print(rest)
输出:
<re.Match object; span=(6, 9), match='san'>
2.1.2 re.match
从字符串头查找匹配项,接受一个正则表达式和字符串,从主串第一个字符开始匹配,并返回发现的第一个匹配。如果字符串开始不符合正则表达式,则匹配失败,re.match返回None。
match.group(default=0):返回匹配的字符串。
group是由于正则表达式可以分拆为多个只调出匹配子集的子组,0是默认参数,表示匹配的整个串,n 表示第n个分。
match.start():
start方法提供了原始字符串中匹配开始的索引。
match.end():
end方法提供了原始字符串中匹配开始的索引。
match.groups():
groups返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
import re
rest = re.match(r"san","hello san")
print(rest)
输出:
None
r'san' 中的r代表的是raw(原始字符串),原始字符串与正常字符串的区别是原始字符串不会将\字符解释成一个转义字符,正则表达式使用原始字符很常见且有用。
2.1.3 注意
re.match()函数只检测RE是不是在string的开始位置匹配,也就是说match()只有在0位置匹配成功的话才有返回, 如果不是开始位置匹配成功的话,match()就返回none, 不能和span()、group()搭配使用,否则会报错。以下的写法是错误的。
search()会扫描整个string查找匹配;search()可以不从0位置开始匹配,这就是和match()的区别。
参考文章:正则表达式match()与search()的区别_Jerry_JD的博客-CSDN博客
2.2 findall 和 finditer 找到多个匹配
2.2.1 re.findall
查找并返回匹配的字符串,返回一个列表。
import re
rest = re.findall(r"a","It's raining cats and dogs")
print(rest)
输出:
['a', 'a', 'a']
2.2.2 re.finditer
查找并返回匹配的字符串,返回一个迭代器。
import re
rest = re.finditer(r"a","It's raining cats and dogs")
print(rest)
输出:
<callable_iterator object at 0x000002D32C74AF10>
2.2.3 区别
- findall 找到所有匹配内容,放到一个列表里返回。
- finditer 找到所有匹配内容,返回一个迭代器,惰性求值。
- finditer 找到每个内容都是一个match对象,需要group来获取里面具体的匹配字符串。
- findall 直接返回匹配上的字符串。
2.3 re.sub('匹配正则' , '替换内容' , 'string')
将string中匹配的内容替换为新内容。
import re
srt1 = "hello, i am learnng python python"
print(re.sub("p.{2}","xixi",srt1))
输出:
hello, i am learnng xixihon xixihon
2.4 编译正则:re.compile('匹配正则')
2.4.1 编译正则的特点
• 复杂的正则可复用。
• 使用编译正则更方便,省略了参数。
• re模块缓存它即席编译的正则表达式,因此在大多数情况下,使用compile并没有很大 的性能优势
m = "my name is jack"
m1 = "my name is rose"
reg = re.compile("is\s(.*)$")
print(reg.findall(m))
print(reg.findall(m1))
输出:
['jack']
['rose']
3、基本正则匹配
3.1 基本正则匹配-区间
最简单的正则表达式是那些仅包含简单的字母数字字符的表达式,复杂的正则可以实现强大的匹配。
区间:[]
正则匹配区分大小写。
import re
ret = re.findall("[12pP]x","2px 1xx Pytho pxoo python2 121Px")
print(ret)
ret1 = re.findall("[a-zA-Z0-9]","dafdasf3224redfr54ewqPOJN")
print(ret1)
输出:
['px', '1x', 'px', 'Px']
['d', 'a', 'f', 'd', 'a', 's', 'f', '3', '2', '2', '4', 'r', 'e', 'd', 'f', 'r', '5', '4', 'e', 'w', 'q', 'P', 'O', 'J', 'N']
3.2 基本正则匹配-或匹配
匹配a或b:a|b。
import re
ret = "sddafds fvguu"
r = re.search("s|u",ret)
print(r.group())
输出:
s
3.3 基本正则匹配-取反
取反:[^abc]
匹配a+非小写字母。
ret = "sddafds fvguu 123"
re1 = re.findall(r"[^A-z]",ret)
print(re1)
输出:
[' ', ' ', '1', '2', '3']
4、正则重复
4.1 贪婪模式和非贪婪模式
贪婪模式(.*):尽可能的匹配长的字符串
非贪婪模式(.*?):尽可能的匹配短的字符串(只要在可能出现贪婪的地方加个 ?:+?、*?、{,4}?)
ret = re.findall("py*?","pyhhhheewipyyyyyyyyyyy")
print(ret)
输出:
['p', 'p']
ret = re.findall("py*","pyhhhheewipyyyyyyyyyyy")
print(ret)
输出:
['py', 'pyyyyyyyyyyy']
5、 正则分组
5.1 简单分组
当使用分组时,除了可以获得整个匹配,还能够获得选择每一个单独组,使用 () 进行分组。
import re
m = "tel:1577-379-3551-55"
ret = re.search(r'(\d{4})-(\d{3})-(\d{4})-(\d{2})',m)
print(ret.group())
print(ret.group(1))
print(ret.group(2))
print(ret.group(3))
print(ret.group(4))
输出:
1577-379-3551-55
1577
379
3551
55
5.2 分组向后引用(引用分组)
使用()分用,用\0, \1, \2引用 (\0表示匹配的整个串)。

'NoneType' object 得到的结果为空(没有返回,NoneType是一个特殊类型,里面只有一个值None)。
5.3 命名分组
命名分组:(?P规则)
import re
# # 命名分组
m = "tel:1577-379-3551-55"
ret = re.search(r'(?P<first>\d{4})-(\d{3})-(\d{4})', m)
print(ret.group())
print(ret.group("first"))
print(ret.groupdict())
输出:
1577-379-3551
1577
{'first': '1577'}
5.4 捕获分组和非捕获分组
捕获分组:
分组之后分配到的数据会放在内存里面,并且给定一个从1开始的索引,捕获分组是可以进行分组向后引用的。
非捕获分组(?:正则表达式):
只分组不捕获,匹配到的分组数据不存放在内存里,不可以进行分组向后引用的
6、正则标记
内联标记:
| 修饰符 | 功能 |
| (?imx) | 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。 |
| (?imx: re) | 在括号中使用i, m, 或 x 可选标志 |
6.1 内联标记: (?imx)re

6.2 内联标记:(?imx: re)

6.3 举例
msg = """you jump, i jump
YOU JUMP, I JUMP"""
# # 对大小写不敏感
# # ret = re.findall(r"jump",msg,re.I)
#
# # 多行模式,影响开始和结束
# ret = re.findall(r"^you jump, i jump$",msg,re.I|re.M)
# print(ret)
# re.S 让.匹配任意字符
ret = re.findall(r"^you.*jump$", msg, re.I|re.S)
print(ret)
7、正则断言
7.1 定义
正则表达式的断言分为:先行断言(lookahead)和后行断言(lookbehind) 正则表达式的先行断言和后行断言一共有4种形式:
- (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
- (?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
- (?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
- (?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertion)
零宽正向先行断言:(?=pattern)
• 代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配pattern。
• 匹配字符串re,后面能接gular(但不匹配宽度)
零宽负向先行断言:(?!pattern)
• 代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配pattern。
• 匹配字符串re,但是后面不能接gular
零宽正向后行断言:(?<=pattern)
• 代表字符串中的一个位置,紧接该位置之前的字符序列能够匹配pattern。
• 匹配gular,但前面必须是re
7.2 举例
import re
# 零宽断言 不占用匹配宽度 确定位置
str1 = "红鲤鱼与绿鲤鱼与驴"
print(re.findall(r".鲤鱼(?=与驴)",str1))#找后面是与驴的鲤鱼
print(re.findall(r".鲤鱼(?!与驴)",str1))#找后面不是与驴的鲤鱼
print(re.findall(r"(?<=红)鲤鱼..",str1))
print(re.findall(r"(?<!红)鲤鱼..",str1))
输出:
['绿鲤鱼']
['红鲤鱼']
['鲤鱼与绿']
['鲤鱼与驴']
8、习题
8.1 找出IP地址
import re
mga = """
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 00:0c:29:a2:f8:96 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.103/24 brd 192.168.0.255 scope global dynamic noprefixroute ens33
valid_lft 86382sec preferred_lft 86382sec
inet6 fe80::5a57:c3e5:6e36:230/64 scope link noprefixroute
valid_lft forever preferred_lft forever
"""
print(re.findall(r"(?<=inet).*(?=brd)",mga))
print(re.findall(r"(?<=ens33).*(?=ens33)",mga, re.I|re.S))
输出:
[' 192.168.0.103/24 ']
[': <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000\n link/ether 00:0c:29:a2:f8:96 brd ff:ff:ff:ff:ff:ff\n inet 192.168.0.103/24 brd 192.168.0.255 scope global dynamic noprefixroute ']
8.2
# # 2.验证邮箱地址,126.com、QQ.com、163.com
com = input("请输入你要验证的邮箱:")
if re.findall(r"\w+@(qq|126|163)[.]{1}com$",com):
print("正确!")
else:
print("没有这个邮箱~")
# 3.
msg = "comy@xx.comcom123@qq.comaaa@126.combbb@163.comcc@abc.com"
print(re.findall(r"(?:\.com)?(\w+@(?:qq|126|163).com)",msg))
# print(ret3.group(1))
ret4 = ".com".strip()
print(ret4)
输出:
请输入你要验证的邮箱:2378996584@qq.com
正确!
['com123@qq.com', 'aaa@126.com', 'bbb@163.com']
.com

797

被折叠的 条评论
为什么被折叠?



