re模块(含正则)
re模块本身知识用来操作正则表达式的,和正则本身没关系。
正则表达式是一种规则,匹配字符串的规则。
1. 正则表达式
1.1 正则规则
1)本身是哪一个字符,就匹配字符中的哪一个字符
2)字符组 [字符或字符范围],匹配字符组中的任意一个字符,只返回一个
字符组还可以匹配范围,所有的范围都必须遵循ascii码从小到大来指定
[0-9] [A-Z] [a-z] [A-Za-z] [0-9x]
特殊匹配用法:
[\d\D] [\W\w] [\S\s] 匹配所有内容,用于匹配以什么开头,后面是任意的东西
3)字符
元字符
|
匹配内容
|
. | 匹配除换行符以外的任意一个字符 |
\w(world) | 匹配一个大小写字母或数字或下划线 |
\s(space) | 匹配任意的空白符:空格,换行符,制表符 |
\d(digital) | 匹配一个数字 |
\n(next) | 匹配一个换行符 |
\t(table) | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ |
匹配字符串的开始,例如 # 匹配以 a 开头
^a
|
$ |
匹配字符串的结尾,例如 # 匹配以 c 结尾
c$
|
\W |
匹配非 字母或数字或下划线 之外的所有字符
|
\D |
匹配非数字
|
\S |
匹配非空白符
|
abc|edf |
匹配字符串abc或字符串edf,两个规则有重叠部分,长的要放前面 |
() |
匹配括号内的表达式,也表示一个组,例如
# 要匹配 www.baidu.com 或者 www.google.com
www\.(baidu|google)\.com
|
[...] |
匹配字符组中的字符
|
[^...] |
匹配除了字符组中字符的所有字符,例如
# 匹配除 abc 之外的所有字符
[^abc]
|
4)量词
量词
|
用法说明
|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
5)贪婪匹配
贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
正则 | 待匹配字符 | 匹配 结果 |
说明 |
<.*> |
<script>...<script> |
<script>...<script> |
默认为贪婪匹配模式,会匹配尽量长的字符串
|
<.*?> | r'\d' |
<script> |
加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串
|
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符 * 是取 0 至 无限长度 ? 是非贪婪模式。 何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在: .*?x 就是取前面任意长度的字符,直到一个x出现
re模块
常用的4种
1. re.search
import re
ret = re.search('\d+','wuye521') print(ret) # 如果能匹配上返回一个对象,如果不能匹配上返回None if ret: print(ret.group()) # 如果是对象,那么这个对象内部实现了group,所以可以取值 # 输出:521 # 如果是None,那么这个对象不可能实现了group方法,所以报错 # 会从头到尾在待匹配的字符串中取出第一个符合条件的值 # 如果匹配到了,返回一个对象,用group取值 # 如果没匹配到,返回None,不能用group
2. re.findall
import re ret = re.findall('\d+','wuye521') print(ret) # 输出:['521'] # findall 会匹配字符串中所有符合规则的项,并返回一个列表 # 如果未匹配到返回空列表
3. re.compile(进阶使用)
使用compile可以降低时间复杂度,所谓时间复杂度就是指你用同样的正则匹配不同的内容,
简写重复解释的部分,让程序只需要解释一次,就是降低时间复杂度,
例如:我们需要用到 匹配文本中的数字
import re r1 = re.search('\d','wuye521') print(r1.group())
r2 = re.findall('\d','luohua520') print(r2)
r3 = re.finditer('\d','wuye521luohua520') for i in r3: print(i.group())
上面我们就重复的使用了 \d 来进行匹配数字,但是我们每种方法里都有这个规则,重复解释了
三次,这个不是浪费了时间嘛,如果更多怎么办?所以接下来使用compile来降低时间复杂度:
import re ret = re.compile('\d3') # 把 正则 表达式编译成python能理解的形式,在后面直接使用 print(ret) # python 理解的正则 r1 = ret.search('wuye521') # 因为python已经编译好了正则表达式,所以这里就不需要再写正则 print(r1.group()) # search 匹配的结果 r2 = ret.findall('luohua520') print(r2) ret = re.compile('\d+') # compile和finditer相结合使用 r3 = ret.finditer('wyue521luohua520') for i in r3: print(i.group())
4. re.finditer(进阶使用)
import re ret = re.finditer('\d','safhl02urhefy023908'*20000000) # ret是迭代器 for i in ret: # 迭代出来的每一项都是一个对象 print(i.group()) # 通过group取值即可
你可能会想这样写用findall不也可以实现嘛,并且还能少写一行代码,比如这样写
ret = re.findall('\d','safhl02urhefy023908'*20000000) print(ret)
其实这里涉及到空间复杂度的问题,什么是空间复杂度呢?简称内存空间,用findall是把所有结果写入内存,
如果你的数据很多很多,那内存一下塞满系统岂不卡死了。用finditer迭代来取,当查询结果超过1个的情况下,
能够有效的节省内存,降低空间复杂度,从而也降低了时间复杂度。
分组的综合使用
使用方法:
# split # 会保留分组中本来应该被切割掉的内容 # 分组命名 # (?P<名字>正则) search group('组名')
# 用法:当需要匹配多个分组内容时,避免被混淆为分组内容命名 # 引用分组 # (?P=组命) 表示这个组中的内容必须和之前已经存在的一组匹配到的内容完全一致
# 用法:匹配html页面标签 # 分组和findall # 默认findall 优先显示分组内的内容 # 取消分组优先显示 (?:正则)
分组命名示例:
import re s1 = '<h1>wuye</h1>' # 不使用分组命名时,要匹配标签和标签中的内容 ret = re.search('<(\w+)>(.*?)</\w+>',s1) print(ret.group()) # ret.group(0)就是它本身匹配的所有内容 print(ret.group(1)) print(ret.group(2)) # 使用分组命名时,要匹配标签和标签中的内容 ret = re.search('<(?P<tag>\w+)>(?P<content>.*?)</\w+>',s1) print(ret.group()) print(ret.group('tag')) print(ret.group('content')) # 输出结果 ''' <h1>wuye</h1> h1 wuye '''
引用分组示例:
import re # 引用分组是为了匹配像 html 标签这样规则的内容 # 比如下面s1中 <h1>内容</h1> 这样的规则 # 如果不是这样,如:<h1>内容</h2> 则不匹配且报错 s1 = '<h1>wuye</h1>' ret = re.search('<(?P<tag>\w+)>.*?</(?P=tag)>',s1) print(ret.group('tag')) # 还有一种不长用的,通过使用转义符进行引用分组,如: s1 = '<h1>wuye</h1>' ret = re.search(r'<(?P<tag>\w+)>.*?</\1>',s1) print(ret.group()) print(ret.group('tag')) # 这样写的话需要注意在正则表达式前面加 r ,否则无法匹配报错
分组和findall示例:
import re ret = re.findall('\d(\d)','aa1alex83') # findall遇到正则表达式中的分组,会优先显示分组中的内容 print(ret) ''' 输出结果:['3'] ''' # 如果我们需要匹配整数或小数时,不小心使用了分组, # 这种情况下我们就需要用到取消优先显示 ret = re.findall('\d+(?:\.\d+)?','1.234+2') print(ret) ''' 输出结果:['1.234', '2'] '''
例题:匹配小数
# 例题 # 有的时候我们想匹配的内容包含在不相匹配的内容当中, # 这个时候只需要把不想匹配的先匹配出来,再通过手段去掉 import re ret=re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))") print(ret) ret.remove('') print(ret) ''' 输出结果: ['1', '2', '60', '', '5', '4', '3'] ['1', '2', '60', '5', '4', '3'] '''
正则的特殊用法:flags的使用
flags有很多可选值: re.I(IGNORECASE)忽略大小写,括号内是完整的写法 re.M(MULTILINE)多行模式,改变^和$的行为 re.S(DOTALL)点可以匹配任意字符,包括换行符 re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用 re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释 # 使用方法:例如:要取h1标签中的文本 import re content = """<head> <h1>affafafd</h1> </head>""" res = re.compile(r'<head>.*?<h1>(?P<cont>.*?)</h1>.*?</head>',flags=re.S) # 这一句为什么要用flags = re.S呢?因为<h1>标签前面有换行符,使用re.S可以匹配换行符了,这里可以也可以使用位置传参代替关键字传参 result = res.search(content) print(result.group('cont'))
不常用的,补充
1. re.match
import re ret = re.match('\d','wuye521') # == re.search('^\d','wuye521') # 两个写法效果相同 print(ret.group()) # 会从头匹配字符串中取出从第一个字符开始是否符合规则 # 如果符合,就返回对象,用group取值 # 如果不符合,就返回None # match = search + ^正则
2. re.split
import re ret = re.split('\d\d\d','wuye521luohua520') # 通过正则规则进行分割 print(ret)
输出结果:
['wuye', 'luohua', '']
为什么会有个空值呢?其实时刚好后面有数字,然后就有空值了,
其实这个分割和字符串的分割是一样的,那如果要保留这个分割的数字呢?
import re ret = re.split('\d(\d\d)','wuye521luohua520') # 默认自动保留分组中的内容 print(ret)
输出结果:
['wuye', '21', 'luohua', '20', '']
3. re.sub
import re # 第一个参数为正则表达式,第二个参数为符合正则要替换成的字符,第三个参数是被替换的内容 ret = re.sub('\d','V','wuye521luohua520') print(ret)
输出结果:
wuyeVVVluohuaVVV
当然也可以指定替换的个数,只需要在第四个参数写上个数即可,例如:
import re ret = re.sub('\d','V','wuye521luohua520',3) print(ret)
输出结果:
wuyeVVVluohua520
4. re.subn
和上面一样,只不过又统计了一下个数,直接看例子:
import re ret = re.subn('\d','O','wuye521luohua520') print(ret)
输出结果:
('wuyeOOOluohuaOOO', 6)
好啦,就先到这里吧!!!
推荐正则学习书籍:《正则指引》python语言
本贴有参考Eva_J