感谢:https://blog.csdn.net/weixin_40907382/article/details/79654372#commentBox
一直被python的正则表达式绕的脑壳疼,看到诸如'#%.*#!~%^&&++'的东西简直是心中一万个烫烫烫 屯屯屯 锟斤拷滚过!!所以决定昨天花一整天的时间弄懂这一块:
首先,使用python的正则表达式需要 import re
三个常用函数:
re.match #从开始位置开始匹配,
re.search #搜索整个字符串,相当于可以不从开始,跳跃匹配
re.findall #搜索整个字符串,返回一个list
这里只列出一些简单常用的表达式,应付一些简单的应用没问题的。比如我要找出这一串字符串里面所有的数字,可能你现在看了也很懵逼,但相信你读完这篇博客就不懵逼了:
s='我昨天吃饭用了45,买水果16.6骑遛遛用 了4块!dajiangyou花了6.06'
cost=re.findall(r'[1-9]+\.?[0-9]*',s)
print(cost)
['45', '16.6', '4', '6.06']
有没有很神奇?...
先列出一些常用的符号:
^re 匹配字符串的开头
re$ 匹配字符串的末尾。
. 匹配任意字符除了换行符,当re.DOTALL标记被指定时,则可以匹配换行符
[...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'
[^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
* 匹配0个或多个的表达式。
+ 匹配1个或多个的表达式。
? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
\.? 表示有一个点或者没有也行(用于匹配浮点数字) # ? 在这里表示有一个或者没有
*? +? ?? 最小匹配
{n} 精确匹配n个前面表达式。
{n,} 最少匹配n个前面表达式。
{n, m} 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a|b 匹配a或b
() 匹配括号内的表达式,也表示一个组
\w 匹配字母数字,汉字和下划线_'''
\W 匹配非字母数字
\s 匹配任意空白字符,等价于 [\t\n\r\f].
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9].
\D 匹配任意非数字
\A 匹配字符串开始
\Z 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。c
\z 匹配字符串结束
\G 匹配最后匹配完成的位置。
\b 匹配一个单词边界
\B 匹配非单词边界。
下面我们看看一些简单的实例:
1)如果^是第一个字符,表示'非':
a='5f1a5FT,sf15f;/4sFyol46l'
b=re.findall(r"[^a-z^0-4]",a) # 找出a中除小写字母和0-4之外的所有字符
>>> ['5', '5', 'F', 'T', ',', '5', ';', '/', 'F', '6']
2)'+'
y=re.findall(r"\d+",a)
>>> ['5', '1', '5', '15', '4', '46'] # ‘+’用于将前面的模式匹配1次或多次(贪婪模式)
y=re.findall(r"\d",a)
>>> ['5', '1', '5', '1', '5', '4', '4', '6'] # 没有加号,表示找到数字就结束,接着继续找下一个数字
3)match
# match 表示从头开始匹配,如果头没有匹配上的就是None,
re.match(r'g','guanyonglai').group() #返回g
re.match(r'y','guanyonglai') #返回None
re.search(r'y','guanyonglai').group() #返回y
4)'|'表示'或': 2019-4-23 13:37:42 # 它在 []之中不再表示或,而表示他本身的字符。
z=re.findall(r'[a-zA-Z]+|[0-9]+', r'alal ,b6al \56 fPython \t Ac\n') # 找出数字或字母(数字和字母分开)
>>> ['alal', 'b', '6', 'al', '56', 'fPython', 't', 'Ac', 'n', 'al']
z=re.findall(r'[a-zA-Z0-9]+', r'alal ,b6al \56 fPython \t Ac\n') # 找出数字和字母(数字和字母可以连一块)
>>> ['alal', 'b6al', '56', 'fPython', 't', 'Ac', 'n']
5)无捕获组 '(?: )' : 2019-4-23 13:56:12
z=re.findall(r'tho(n|p)', r'alal ,b6al \56 fPython \t Ac\n') # tho(n|p) 这样写达不到我们的要求,应当用误捕获组
>>> ['n']
z=re.findall(r'tho(?:n|p)', r'alal ,b6al \56 fPython \t Ac\n')
>>> ['thon']
6)匹配行首行尾,头尾
#匹配行首的字母,支持跨行 2019-4-23 14:15:33
z=re.findall(r'^[a-z]+', 'alal ,b6al \56 \nfPython \t Ac\n',re.M)
>>> ['alal', 'f']
z=re.findall(r'\d+$', 'alal ,b6al 56\nfPython\t4Ac\65',re.M) # 匹配行尾的数字(\6可能是个bug吧,匹配不到)
>>> ['56', '5']
# \A 只匹配整个字符串的开头,即使在 ’M’ 模式下,它也不会匹配其它行的行首。 2019-4-23 14:29:06
z=re.findall(r'\A[a-z]+', 'alal ,b6al \56 \nfPython \t Ac\n',re.M)
>>> ['alal']
7)单词的边界\b: 2019-4-23 14:48:25
z=re.findall(r'\bth\b', 'alal ,b6 th al 56\nfPython\t4Ac\65') # 找到单独的'th',,,\B相反:不能匹配以'th'为边界的字符串
>>> ['th']
z=re.findall(r'(?:\w+|\s+)th(?:\w+|\s+)', 'alal ,b6 th al 56\nfPython\t4Ac\65') # 找到所有含'th'的单词(\w包括数字和大小写字母)
>>>[' th ', 'fPython']
8)如何在正则表达式中包含变量: 2019-4-23 15:24:10
aa='th'
ss=re.compile(r'\b%s\b'%aa)
z=re.findall(ss, 'alal ,b6 th al 56\nfPython\t4Ac\65')
>>> ['th']
9)汉字代码:[\u4e00-\u9fa5] 2019-4-23 16:16:39
10)注释:(?#) ’(?#’ 与‘)’ 之间的内容将被忽略。如 (?#abcdefg)
11)重复匹配,*表示匹配前面的规则0或多次,+表示匹配前面的规则1或多次,,2019-4-23 17:08:41
s='aaa bbb111 cc22cc 33dd '
zz=re.findall( r'\b[a-z]+\d*\b' , s ) # 必须至少 1 个字母开头,以连续数字结尾或没有数字,,(为何不能把*放前面?因为*表示匹配前面的规则)
>>> ['aaa', 'bbb111']
12)'''注意!注意!注意!注意!注意!:''' 2019-4-23 17:52:13
s='123 10e3 20e4e4 30ee5'
zz=re.findall( r'\d+\w+\b' , s ) # 以多个数字或字母结尾,且前面是数字
>>> ['123', '10e3', '20e4e4', '30ee5']
||
zz=re.findall( r'\d+\w\b' , s ) # 以一个数字或字母结尾,且倒数第二个是数字,所以只有123
>>> ['123']
s='1 22 333 ert4444 55555 666666' 2019-4-24 08:57:07
zz=re.findall( r'\b\w\b' , s ) #为啥只有1?他的意思是不是以一个\w开头,并以该\w结尾,所以只能是一个??
>>> ['1']
s='&555撒地方55$ 666666'
zz=re.findall( r'\b\W+.*?\W+\b' , s )
>>> ['$ '] # 这里输出的为啥不是&555撒地方55$?问得好!因为这里要找的是\W,开头和结尾不再以空格来判断,而是以字母数字汉字,这里的字母数字汉字就相当于\w里的空格!!!
||
#下面我们把&555撒地方55$头尾都加数字,现在的输出就是我们期盼的了,
s='2&555撒地方55$2 666666'
zz=re.findall( r'\b\W+.*?\W+\b' , s )
>>> ['&555撒地方55$']
????? # 前面的DBPNE也属于字母为啥没检测到?? 2019-4-24 09:54:29
s='K71U8DBPNE-eyJsaWNlbnNlSWQiOiJLNzFVOERCUE5FIiwibGljZW5zZWVOYW1lIjoibGFu'
zz=re.findall( r'\b[A-Za-z-_]{10,}' , s )
>>> ['-eyJsaWNlbnNlSWQiOiJLNzFVOERCUE']
# 因为我们需要查找10个以上字母开头的串,既然是开头,那么就需要找到空格等空字符,而'-'也可以作为空字符(虽然我们查找的内容包含了'-',但它作为空字符标识这个身份并没有改变)
data='2.35 6 56adf6.f336.65ff '
oneday=re.findall(r"\d+\.?\d*",data) # \.? 表示有一个点,或者没有(常用语找小数点2019-4-24 14:42:20)
>>> ['2.35', '6', '56', '6.', '336.65']
'''注意!注意!注意!注意!注意!'''
13)精确匹配 2019-4-23 18:02:21
# {m,n}表示匹配最少 m 次,最多 n 次(n>m),{m}表示只匹配m次,{m,}表示最少匹配m次,{,n}表示最多匹配n次,
s='1 22 333 4444 55555 666666'
zz=re.findall( r'\b\d{3,}\b' , s ) # 匹配出三位及以上的数字
>>> ['333', '4444', '55555', '666666']
14)最小匹配 ‘*?’ ‘+?’ ‘??’ 2019-4-23 18:21:14
s='/* part 1 */ code /* part 2 */' # 这是C语言的注释,/**/里面的内容表示注释的内容,
zz=re.findall( r'/\*.*\*/' , s ) # 匹配以/*开头以*/结尾的字符串,默认是尽可能多地匹配字符,所以结果是把两部分的注释连在一起了(在正则表达式里*有别的含义,这里用\*转义一下表示*本身)
>>> ['/* part 1 */ code /* part 2 */']
zz=re.findall( r'/\*.*?\*/' , s ) # *?表示尽可能少地匹配,
>>> ['/* part 1 */', '/* part 2 */'] # 尽可能少地匹配,使得两部分注释分开了,
15)前向界定与后向界定 2019-4-23 19:31:29
'(?<=...)' 前向界定: ...代表你希望匹配的字符串前面应该出现的字符串。 #前向界定括号中必须是常值,不能是正则表达式
'(?=...)' 后向界定:同理
s='/* part 1 */ code /* part 2 */'
zz=re.findall( r'(?<=/\*).*?(?=\*/)' , s )
>>> [' part 1 ', ' part 2 '] # 相比于14),我们只匹配注释部分的内容而不要注释符号,
16)前向非界定yu后向非界定,和15)相反,2019-4-23 19:54:11
'(?>> ['111'] # 返回的是111而不是aaa111aaa,因为我们把\d括起来了,(\d+)就是一个组,
?P 给一个组命名
?P=name 调用已匹配的命名过的组
s='aaa111aaa,bbb222,333ccc,444ddd444,555eee666,fff777ggg'
zz=re.findall( r'([a-z]+)\d+([a-z]+)' , s )
>>> [('aaa', 'aaa'), ('fff', 'ggg')] # 找到包夹数字的字母,
zz=re.findall( r'(?P[a-z]+)\d+(?P=g1)' , s ) # 前面给字母串命名g1,后面查找名为g1的字母串,2019-4-24 10:22:59
>>> ['aaa'] 找出被中间夹有数字的前后同样的字母('''注意是前后同样的字母''')
zz=re.findall( r'([a-z]+)\d+\1' , s ) # 意义同上,只不过这里用\1表示前面的命名组(每个命名组都有一个序号)
>>> ['aaa']
s='111aaa222aaa111 , 333bbb444bb33'
zz=re.findall( r'(\d+)([a-z]+)(\d+)(\2)(\1)' , s ) # 找出完全对称的 数字-字母-数字-字母-数字 中的数字和字母
>>> [('111', 'aaa', '222', 'aaa', '111')]
||
‘(?( id/name )yes-pattern|no-pattern)’ 判断指定组是否已匹配,执行相应的规则 # 条件匹配没太弄明白 2019-4-24 10:51:59
||
18)compile()规则预编译 2019-4-24 11:04:04 # 作用:可以加速
s='111,222,aaa,bbb,ccc333,444ddd'
rule=r’/b/d+/b’
compiled_rule=re.compile(rule)
compiled_rule.findall(s)
>>> ['111', '222']
预编译作用:#直接使用 findall ( rule , target ) 的方式来匹配字符串,一次两次没什么,如果是多次使用的话,
#由于正则引擎每次都要把规则解释一遍,而规则的解释又是相当费时间的,所以这样的效率就很低了。如果要多次使
#用同一规则来进行匹配的话,可以使用 re.compile 函数来将规则预编译,使用编译过返回的 Regular Expression Object 或叫做 Pattern 对象来进行查找。
19)match 与 search 2019-4-24 11:43:00 两者区别:match 从字符串的开头开始匹配,如果开头位置没有匹配成功,就算失败;search 会继续向后寻找是否有匹配的字符串
s= 'Tom:9527 , Sharry:0003'
m=re.match( r'(?P\w+):(?P\d+)' , s ) # 注意有(?P\w+)和(?P\d+)两个组,(?P\w+)是组1,(?P\d+)是组2,组0是整体
print(m.group())
>>> Tom:9527
print(m.groups())
>>> ('Tom', '9527')
print(m.group(0))
>>> Tom:9527
print(m.group(1))
>>> Tom
print(m.group(2))
>>> 9527
print(m.group('name'))
>>> Tom
print(m.groupdict())
>>>{'name': 'Tom','num': '9527'}
20)finditer( rule , target [,flag] ) 返回一个迭代器,参数同 findall
s='111 222 333 444'
for i in re.finditer(r'\d+' , s ):
print (i.group(),i.span())
>>> 111 (0, 3)
222 (4, 7)
333 (8, 11)
444 (12, 15)