python基础之坑爹正则表达式
python基础之坑爹正则表达式
概述
re模块就是python语言中的正则表达式,拆出来单独写一条blog是因为正则表达式本身就是比较庞大的知识,写具体些让自己以后方便查找。
IP:
^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$
手机号:
^1[3|4|5|8][0-9]\d{8}$
由于在python中,“\”也被定义为转义字符,因此两个python中的“\”才能代表一个正则中的“\”,这就导致了大量的“\”重复。为了解决这一问题,python提供了原生字符的办法。也就是在字符串前面加上一个“r”,代表此字符串中的“\”可直接用于正则表达式,而不用再次转义。因此,请养成在python的正则表达式字符串的前面添加一个“r“的好习惯。
字符
字符匹配分为普通字符和元字符.
- 大多数字符都会和自身匹配
- 元字符,元字符在[]中没有特殊意义,元字符包括:
元字符 | 含义 |
---|---|
. | 匹配除换行符外的所有字符 |
\w | 匹配字母或者数字或者下划线或汉字 |
\W | 匹配任何非字母或数字或下划线,汉字 |
\s | 匹配任意的空白符,相当于[ \t\n\r\f\v] |
\S | 匹配任意非空白字符 |
\d | 匹配任意十进制数字 |
\D | 匹配任意非数字字符 |
\b | 匹配单词的开始或者结束,即单词和空格的位置,只是匹配字符串开头结尾及空格回车等的位置, 不会匹配空格符本身 |
^ | 匹配字符串的开始 |
匹配字符串的结束 | |
* | 重复0次或更多次 |
+ | 重复1次或多次 |
? | 重复0次或1次 |
重复n次 | |
重复n次或更多次 | |
重复n到m次 | |
[] | 用来指定一个字符集和范围,字符可以单个列出,也可以用“-”号分隔的两个给定.字符来表示一个字符区间。例如,[abc] 将匹配"a", "b", 或 "c"中的任意一个字符;也可以用区间[a-c]来表示同一字符集,和前者效果一致 |
|反斜杠跟后面的元字符,去除元字符的特殊功能;与普通字符一起,实现特殊功能. | |
() | 分组功能 |
字符中需要注意的事项
- 如果元字符存在于[]中的字符类中时,只有字符^,-,\有特殊含义.其他无特殊意义.例如:
例如,[akm\(]将匹配字符"a", "k", "m", 或 "\)" 中的任意一个;"$"通常用作元字符,但在字符类别里,其特性被除去,恢复成普通字符。
元字符 | 含义 |
---|---|
|仍表示转义 | |
- | 表示范围 |
^ | ^放在支付前面,表示非 |
- *?,+?,??,{m,n}? 前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配,后面加个问号,将策略改为非贪婪,只匹配尽量少的RE,来个例子吧:
>>> re.findall(r"a(\d+?)","a23b") # 非贪婪模式
['2']
>>> re.findall(r"a(\d+)","a23b")
['23']
- \b 就是用在你匹配整个单词的时候。 如果不是整个单词就不匹配。 你想匹
配 I 的话,你知道,很多单词里都有I的,但我只想匹配I,就是“我”,这个时
候用 \bI\b
re模块中的函数
函数 | 描述 | 返回值 |
---|---|---|
compile(pattern[, flags]) | 根据包含正则表达式的字符串创建模式对象 | re模块的对象 |
search(pattern, string[, flags]) | 在字符串中寻找模式 | 第一个匹配到或者None |
match(pattern, string[, flags]) | 在字符串的开始处匹配模式 | 在字符串开头匹配的对象或者None |
split(pattern, string[, maxsplit=0,flags]) | 根据模式的匹配项来分割字符串 | 分割后的字符串列表 |
findall(pattern, string,flags) | 列出字符串中模式的所有匹配项 | 所有匹配到的字符串列表 |
sub(pat,repl, string[,count=0,flags]) | 将字符串中所有的pat的匹配项用repl替换 | 完成替换后的新字符串 |
finditer(pattern, string,flags) | 将所有匹配到的项生成一个迭代器 | 所有匹配到的字符串组成的迭代器 |
subn(pat,repl, string[,count=0,flags]) | 在替换字符串后,同时报告替换的次数 | 完成替换后的新字符串以及替换的次数,返回值为元组数据 |
escape(string) | 将字符串中所有特殊正则表达式字符串转义 | 转义后的字符串 |
purge(pattern) | 清空正则表达式 | |
template(pattern[,flags]) | 编译一个匹配模板 | 模式对象 |
fullmatch(pattern, string[, flags]) | match函数的全字符串匹配版本 | 类似match的返回值 |
compile
将正则表达式转换为模式对象,提高工作效率。使用compile转换后,以后在每次模式使用时直接调用即可!经过compile转换的正则也能使用普通的re函数。
import re
res=re.compile(r'abc')
print(res.match('abcde'))
print(res.match('abcd123').group)
print(res.match('abcd123').group())
输出:
<_sre.SRE_Match object; span=(0, 3), match='abc'>
<built-in method group of _sre.SRE_Match object at 0x0000016911B395E0>
abc
从输出的结果我们可以看出来,compile处理后,返回的是re对象。事实上,compile是在调用match,findall函数之前默认优先编译的一步,所有我们可以在重复调用的某匹配的时候,可先将re.compile赋值于一个变量。
match
match对指定字符串的开头进行匹配,匹配成功后返回一个match的object,如果匹配不成功返回None!
res=re.match(r'abc','123')
print(res)
res1=re.match(r'abc','abc23')
print(res1)
print(res1.start())
print(res1.end())
print(res1.span())
print(res1.group())
out:
None
<_sre.SRE_Match object; span=(0, 3), match='abc'>
0
3
(0, 3)
abc
out中span表示匹配到数字的下标范围,从0到2,所以与列表、字典中取值是相同的
group()是用来直接查看匹配的结果的
search
search用来在指定字符串中查找,返回值为第一个匹配到的字符串
使用方法和match类似
res=re.search(r'abc','123abcsdfsdfsdfabc')
print(res.group())
print(res.start())
print(res.end())
print(res.span())
结果:
abc
3
6
(3, 6)
findall
findall是搜索指定字符串中的所有指定匹配项,返回值为一个列表,如果没有匹配项,那么返回的是一个空列表,而且,findall不需要group()
res=re.findall(r'efg','12efg,sdf,efgsdfsd')
print(res)
res1=re.findall(r'efg','12asdfasdfasdf')
print(res1)
out:
['efg', 'efg']
[]
split
split类似字符串中的分割,用来指定字符串为分隔符,将匹配目标分割成列表形式。而且split有一个maxsplit的参数,用来指定分割的次数。
s='1*8+2-6*-10'
res=re.split(r'[\+\-\*\/]',s)
print(res)
res1=re.split(r'[\+\-\*\/]',s,maxsplit=3)
print(res1)
out:
['1', '8', '2', '6', '', '10'] #1
['1', '8', '2', '6*-10']
注意看结果,#1标志中,为什么第一行的结果,索引-2会是一个空值呢,,是因为"6*-10",将*切割,10前面的负号和星号中间为空值。
而利用re模块中牛逼哄哄的分组功能,能将分割出来的字符串包括分隔符都加入列表中。来看下:
s='1*8+2-6*-10'
res2=re.split(r'([\+\-\*\/])',s)
print(res2)
out:
['1', '*', '8', '+', '2', '-', '6', '*', '', '-', '10']
sub
sub类似字符串中的replace功能,用指定的内容替换匹配到的字符或字符串,同时也可以指定替换次数,使用count= 来指定,或者直接数字也可。
s='hello,I am cc!What the fuck day!'
res=re.sub(r'a','xxx',s)
print(res)
res2=re.sub(r'a','xxx',s,count=2)
print(res2)
out:
hello,I xxxm cc!Whxxxt the fuck dxxxy!
hello,I xxxm cc!Whxxxt the fuck day!
subn
看代码吧,同上,只不过比sub多了替换次数,返回值为元组
s='hello,I am cc!What the fuck day!'
res=re.subn(r'a','xxx',s)
print(res)
print(type(res))
out:
('hello,I xxxm cc!Whxxxt the fuck dxxxy!', 3)
<class 'tuple'>
group和groups
groups返回元组,group返回的是字符串
直接看代码吧:
a = "123abc456"
print(re.search(r"([0-9]*)([a-z]*)([0-9]*)", a).groups())
print(re.search(r"([0-9]*)([a-z]*)([0-9]*)", a).group())
print(re.search(r"([0-9]*)([a-z]*)([0-9]*)", a).group(0))
print(re.search(r"([0-9]*)([a-z]*)([0-9]*)", a).group(1))
print(re.search(r"([0-9]*)([a-z]*)([0-9]*)", a).group(2))
print(re.search(r"([0-9]*)([a-z]*)([0-9]*)", a).group(3))
out:
('123', 'abc', '456')
123abc456
123abc456
123
abc
456
flag编译标识
flag为re匹配时的匹配模式,有X I M S A L U,7种模式.
标识符 | 全拼 | 作用 |
---|---|---|
I | IGNORECASE | 忽略大小写 |
L | LOCALE | 使\w, \W, \b, \B, \d, \D依赖于本地设置 |
M | MULTILINE | 让正则表达式的^和$符号可以适应多行模式的字符串 |
X | VERBOSE | 注释模式 |
A | ASCII | 对于字符串,使得\w, \W, \b, \B, \d, \D只匹配ACSII码字符集,而不是整个Unicode字符集(默认);对于bytes模式,这个编译标志是默认设置,不需要特别指定。 |
S | DOTALL | 使 "." 特殊字符完全匹配任何字符,包括换行;没有这个标志, "." 匹配除了换行外的任何字符 |
U | UNICODE | 兼容模式。在字符串模式下被忽略(默认模式),在字节模式下被禁止 |
M说明下:
s='\n123\n'
res=re.search(r'^123',s)
print(res.group())
这么执行会报错.
但如果使用下面re.M就会正常:
res1=re.search(r'^123',s,re.M)
print(res1.group())
分组
先上例子:
>>> p = re.compile('(a(b)c)d')
>>> m = p.match('abcd')
>>> m.group(0)
'abcd'
>>> m.group(1)
'abc'
>>> m.group(2)
'b'
分组是在re中比较牛逼的东西,功能:去已经匹配到的结果中再提取数据,相当于二次过滤.
提取分组结果使用group groups groupdict
先来看下无分组的结果:
s='abc23434efdabceab'
res=re.match(r'a\w+',s)
print(res.group())
print(res.groups())
print(res.groupdict())
out:
abc23434efdabceab
()
{}
再来看有分组的:
s='abc23434efdabceab2'
res=re.match(r'a(\w+).*(?P<name>\d)$',s)
print(res.group())
print(res.groups())
print(res.groupdict())
out:
abc23434efdabceab2
('bc23434efdabceab', '2')
{'name': '2'}
解释下:匹配时是按r''中的整体去匹配的,第一次匹配完之后,然后再用括号去匹配.有两个括号,那就是从匹配到的结果中,再过滤出两个匹配的结果.再看个例子:
s='abc23434efdabceab2'
res=re.search(r'a(\w+).*(?P<name>\d)$',s)
print(res)
print(res.group())
print(res.group(0))
print(res.group(1))
print(res.group(2))
print(res.groups())
print(res.groupdict())
out:
<_sre.SRE_Match object; span=(0, 18), match='abc23434efdabceab2'>
abc23434efdabceab2
abc23434efdabceab2
bc23434efdabceab
2
('bc23434efdabceab', '2')
{'name': '2'}
可以看出来,group 结果的字符串,groups是元组,groupdict是字典.在group结果中,获取分组结果时,取括号的索引即可,第几个就是group(几),group(0)是第一次匹配出的结果本身
findall,split中的分组有歧义
origin = "hello alex bcd alex lge alex acd 19"
r = re.split("a(le)x", origin)
print(r)
out:
['hello ', 'le', ' bcd ', 'le', ' lge ', 'le', ' acd 19']
s='abc1223434efdabc34121224eabc12'
res=re.findall(r'a(bc)(12)*',s)
print(res)
out:
[('bc', '12'), ('bc', ''), ('bc', '12')]
sub中没有分组的概念
s='abc1223434efdabc34121224eabc12'
res=re.sub(r'a(bc)(12)*','xxx',s)
print(res)
out:
xxx23434efdxxx34121224exxx
看来并没有什么乱用!