import re
re1 = re.compile(r'元字符 组成的正则规则') # 元字符下面会说
re1. 方法 () # 方法下边也会说
表示普通字符:. # 除了 \n 外 都可以匹配的到
\d # 只匹配 纯数字 0-9
\D # 和 \d 相反,除了数字全都匹配
\s # 只匹配空格
\S # 和 \s 相反,除了空格,全都匹配 # 我喜欢用 [\s\S]*? 匹配所有
\w # 只匹配 纯数字 或 大小写字母 或 下划线
\W # 与 \w 恰好相反,除了 纯数字、大小写字母、下划线 全都匹配
[] # [abcde] 只要包含这个列表的字符,都可以匹配的到。但默认只取一个,简写 [a-e]
eg: re.compile(r'[e-h]').match('hello python').group(0)
>>> h
此外: [^abcde] 或 [^a-e] 表示 '排除',意思就是 除了 abcde 全匹配
匹配表示边界的:^ # 匹配 起始 位置,受 re.M 影响 #注意:不要和 [^123] 除 123 之外搞混
eg:
import re
r1 = re.compile(r'^\d+')
print(r1.search('456hello123').group())
>>> 456
$ # 匹配 结尾 位置,受 re.M 影响
eg:
import re
s = """
123abc456
678abc789
"""r1 = re.compile(r'\d+$',re.M) # 注意这里加入了 re.M
print(r1.findall(s))
>>> ['456', '789'] # 这是写了 re.M,就意味着 每一行都给你单独按照规则处理
>>> ['789'] # 如果没写 re.M, 那么就按照整体,去最后一行的尾部
注:其实 re.M 的本质是 是根据 \n,进行 断行,断行后对每一行按照规则单独处理
\b:# 匹配 单词的 边界(除了 数字、中英字母、下划线 的 所有符号)eg:
import re
s = '你好啊 ---- 好个 P'
r1 = re.compile(r'\b 好')
print(r1.findall(s))
>>> 好
# 解释:这个‘好’是,后面 的那个。因为后面的’好‘字 左边是符号,而非单词字符
\B:# 匹配 单词 非 边界(包括 数字、中英字母、下划线)eg:
import re
s = '你好啊 ---- 好个 P'
r1 = re.compile(r'\b 好')
print(r1.findall(s))
>>> 好
# 解释:这个‘好’是,前面 的那个。因为前面的’好‘字 左边是中文字符。属于非边界
# 所以就匹配上了
再次总结:\b 与 \B:\b:匹配边界字符。边界字符:(除了 数字、字母、汉字、下划线的所有符号)\B:匹配非边界字符。非边界字符:(数字、字母、汉字、下划线)匹配表示数量的:*:0 次 或 多次 eg:你 *
+:1 次 或 多次 eg: 你 +
?:0 次 或 一次 eg: 你?
{m} : 出现 m 次 eg: 你 {3}
{m,} : 至少 出现 m 次 eg: 你 {3,} # 涉及到贪婪模式,不深的不要用
{m,n}: m 次 到 n 次 之间任意一次就行 eg: 你 {3,6}
表示分组:|:相当于或运算符,两边写的是 正则表达式,优先选择左边的
() : 括起来里面的内容,就变成了分组。可以用 .group(1) 提取,如果有更多那就 group(2)..
(?P<name>):在上面分组的基础上 起别名
(?P=name) : 根据分组的别名来使用分组
eg:
s = '<h1> 你好 </h1>'
r1 = re.compile(r'<(?P<name1>\w+)>(\w+)</(?P=name1)>').match(s).group(2)
print(r1)
>>> 你好
\ 数字:提取的分组可以在 同一个正则中 复用
eg:
s = '<h1> 你好 </h1>'
r1 = re.compile(r'<(\w+)>(\w+)</\1>') # \1 代表复用第一个分组
print(r1.match(s).group(2)) # 2 代表提取第二个分组
>>> 你好
re.M # 多行匹配,影响 ^ 和 $,上面讲 ^ 与 $ 已经详解了。re.I # 忽略大小写
eg:
s = 'aAbB'
r1 = re.compile(r'aabb', re.I).match(s).group()
print(r1)
>>> aAbB
re.S # 提升 . 的权限,让 . 可以 匹配到换行符
s = """
hello
python
"""r1 = re.compile(r'.*', re.S).match(s).group() # 注意这里 re.S
print(r1)
>>> hello
python
注意:如果不写 re.S 那么 .* 只能匹配到第一行的空字符串,因为遇到第一个空行的 \n 就停止了
re.X # 可以给正则分行写,并可以加注释,eg:
import re
title = '1 好 2 你 3'
r1 = re.compile(r"""
1 # 注释 1 看这两行
好 # 注释 2 看这两行,1 和 好 没有加逗号。但是他们属于整体的规则,你可以加注释
""", re.X) # 把正则可以分行写, 用了 re.X 后,分行的正则会被看作为一行
result = r1.match(title).group()
print(result) # 输出结果:1 好
个人理解:贪婪模式:(Python 默认使用的就是 贪婪模式)
你想匹配 一个句子中的 一个单词,但是你写的规则恰好可以 满足 匹配所有单词。那么它就会 贪婪的 把所有单词 全部 都给你匹配出来。(贪)
使用方法:* 或 +
非贪婪模式:即使你把规则写的很好,并且能把所有字符串都匹配到,但是如果你加上了 非贪婪模式。在满足规则条件的前提下,只匹配一个.
使用方法:*? 或 +?
eg1:基于 search 的贪婪模式(match 同此)我们先回忆一下:search() 方法的 最核心思想就是:从前往后搜,搜到一个满足的就直接返回。OK,继续。贪婪:(默认):import re
r1 = re.compile(r'\d+')
print(r1.search('你好 333 你好 333 你好').group())
>>> 333 # 满足规则后 尽可能贪,所以第一串连着的 '333' 搜到了就直接返回了
非贪婪(就多了个问号 ?):import re
r1 = re.compile(r'\d+?')
print(r1.search('你好 333 你好 333 你好').group())
>>> 3 # 嗯,你的规则就是 至少一个数字,搜到了一个就可以返回了,干得漂亮。eg2: 基于 findall 的贪婪模式(如果你 findall 与规则,理解的不透彻,这个会有点绕的,前方高能)先回忆一下:findall() 方法的 最核心思想就是:拿着 定死的 规则,把所有满足规则的都提出来
OK,继续。贪婪(默认):import re
r1 = re.compile(r'\d+')
print(r1.findall('你好 333 你好 333 你好'))
>>> ['333', '333']
解释:规则是匹配至少一位数字。但是 贪婪模式 提醒了 规则:“你的任务是给我尽可能的 多匹配数字”findall 拿着 被贪婪化的 规则 去匹配原始字符串
被贪婪模式 提醒过的规则果然不负众望,一次提一串连着的‘333‘findall 拿着它 提取了 两次 , 就把所有数字提取出来了
结果就是 ['333', '333']
非贪婪:import re
r1 = re.compile(r'\d+?')
print(r1.findall('你好 333 你好 333 你好'))
>>> ['3', '3', '3', '3', '3', '3']
解释:规则 同样是 匹配至少一位数字。但是 非 贪婪模式 提醒了 规则:“你的任务是给我尽可能的 少 匹配数字”findall 拿着 被贪婪化的 规则 去匹配原始字符串
被贪婪模式 提醒过的规则果然不负众望,一次只提取一个‘3‘findall 拿着它 提取了 六次 , 才把所有数字提取出来了
结果就是 ['3', '3', '3', '3', '3', '3']
match():
'''match() 方法是 根据规则从第一个开始,向后逐个匹配,如果有一个字符匹配不上,就返回 None'''
s = 'hello python'
re1 = re.compile(r'he')
re1.match('')
result = re1.match(s).group() if re1.match(s) else None # 注意:非 None 才有 group 方法
print(result) # 通过 group() 方法获得的才是最终 正则匹配的字符串
>>> he
简单分组提取:s = 'hello python'
re1 = re.compile(r'h(e)llo') # 给 e 加个一个 (), 就代表添加了分组,一会要把他提出来
result = re1.match(s).group(1) if re1.match(s) else None
# 注意上方的 group(1) 这个参数是 1,代表 只 提取 分组 里面的内容
>>> e
# 如果是 group() 或 group(0) 代表提取 整个正则规则 的内容
>>> hello
print(result)
>>> e
嵌套 - 平行(深度 - 广度)分组提取:原理:分组提取先提取嵌套的,后提取平行的 (专业点就是先深度,后广度)
eg:a = '123-%%%-dd'
result = re.compile(r'123(-(%%%)-)d(d)').match(a).groups()
# 或者用 group(1), group(2), group(3) 代替 groups() 单个看也行
print(result)
>>> ('-%%%-', '%%%', 'd')
search():
"""search() 方法是:从前向后按规则‘搜索’, 直到搜到位置,搜不到就返回 None"""
s = "aaa123aaa"
r1 = re.compile(r'\d+').search(s).group()
print(r1)
>>> 123
findall():
"""findall() 方法是:按照正则规则,搜索所有符合规则的字符串,以列表的形式作为结果返回"""
s = "aaa---123---bbb"
r1 = re.compile(r'\w+').findall(s)
print(r1)
>>> ['aaa', '123', 'bbb']
微不足道的扩展:a = '123-%%%-dd'
result = re.compile(r'-(.*?)-').findall(a)
print(result)
>>> %%%
# 解释:findall() 方法中 如果规则中含有分组,那么就会只返回分组中提取的的内容
finditer():
"""finditer() 和 findall() 使用方式一样,只不过返回结果是 可迭代对象,easy, 此处不在多说"""
split():
"""split() 方法是:按照规则去切割,切割结果以列表的方式返回"""
语法关联:我们知道字符串 有 split() 方法,可以按照一个参数损耗来切割,但是这个参数只能指定一个
如果让你在多种规则的前提下切割,需要怎么办。巧了,正则切割 split() 方法就是解决这个问题的,实例如下:s = "aaa%%123@@bbb" # 可以看见,% 和 @符号把字符分开了,现在我们只想要字符
r1 = re.compile(r'\W+').split(s) # \W 大写:以非单词性字符作为损耗规则,来切割
print(r1)
>>> ['aaa', '123', 'bbb']
sub():
"""sub() 方法是:按照规则匹配选出代替换的字符,然后自己 给定字符去替换"""
场景 1:常用方式,自己给定目标字符串,按规则匹配并直接替换原始字符串
eg:
s = "aaa%%123@@bbb"
r1 = re.compile(r'\W+').sub('你好',s)
print(r1)
>>> aaa 你好 123 你好 bbb
场景 2:正则匹配后的结果 经过函数操作,函数的返回值作为 替换的最终结果
eg:
s = "aaa%%123@@bbb"
r1 = re.compile(r'\W+').sub(lambda a:a.group()*2, s)
print(r1)
>>> aaa%%%%123@@@@bbb
解释:按照规则匹配到的字符是 %% 和 @@,经过函数 乘以 2 后,就替换成了 %%%% 和 @@@@
subn():
"""subn() 和 sub() 语法几乎一样,唯一的扩展功能就是 返回结果是元组,(字符串, 次数)"""
s = "aaa%%123@@bbb"
r1 = re.compile(r'\W+').subn('你好',s)
print(r1)
>>> ('aaa 你好 123 你好 bbb', 2)