玩转python的正则表达式|提取字符串中的所有数字

感谢: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
       '(?<!...)' 前向非界定:...代表你希望匹配的字符串前面不要出现的字符串。
       '(?!...)'后向非界定:同理

     17)组的基本知识:  2019-4-24 10:05:47
       s='aaa111aaa , bbb222 , 333ccc'
       zz=re.findall( r'[a-z]+(\d+)[a-z]+' , s )
       >>> ['111']   # 返回的是111而不是aaa111aaa,因为我们把\d括起来了,(\d+)就是一个组,
       
       ?P<name> 给一个组命名
       ?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<g1>[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<name>\w+):(?P<num>\d+)' , s ) # 注意有(?P<name>\w+)和(?P<num>\d+)两个组,(?P<name>\w+)是组1,(?P<num>\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)

 

    原文作者:guanyonglai
    原文地址: https://blog.csdn.net/guanyonglai/article/details/89512659
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞