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用来在指定字符串中查找,返回值为第一个匹配到的字符串
使用方法和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

看来并没有什么乱用!

posted @ 2016-06-14 18:17  ccorz  阅读(472)  评论(0编辑  收藏  举报