zoukankan      html  css  js  c++  java
  • Python自动化运维之8、正则表达式re模块

    re模块

      正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串,在文本处理方面功能非常强大,也经常用作爬虫,来爬取特定内容,Python本身不支持正则,但是通过导入re模块,Python也能用正则表达式,下面就来讲一下python正则表达式的用法。正则表达式默认以单行开始匹配的

    一、匹配规则

    二、re的相关方法

    re.findall()

    findall(),可以将匹配到的结果以列表的形式返回,如果匹配不到则返回一个空列表,下面来看一下代码中的使用

    import re

    def re_method(): s1 = 'Hello, this is Joey' s2 = 'The first price is $9.90 and the second price is $100' print(re.findall(r'w+',s1)) print(re.findall(r'd+.?d*',s2)) if __name__ == '__main__': re_method()

    re.finditer()

    可以将匹配到的结果生成一个迭代器

    import re
    
    def re_method4():
        # finditer
        s2 = 'The first price is $9.90 and the second price is $100'
        i = re.finditer(r'd+.?d*',s2)
        for m in i:
            print(m.group())
    
    if __name__ == '__main__':
        re_method4()
    

    re.search()

    search是匹配整个字符串直道匹配到一个就返回。

    import re
    
    def re_demo():
        txt = 'If you puchase more than 100 sets, the price of product A is $9.90.'
        m = re.search(r'(d+).*$(d+.?d*)',txt)
        print(m.groups())
    
    if __name__ == '__main__':
        re_demo()
    

    re.match()

    match从要匹配的字符串的开头开始,尝试匹配,如果字符串开始不符合正则表达式,则匹配失败,函数返回None,匹配成功的话用group取出匹配的结果

    import re
    
    def re_method():
        # search vs match
        s = 'abcdc'
        print(re.search(r'c',s))  #search是从开头到结尾的匹配到第一个匹配的
        print(re.search(r'^c', s))
        print(re.match(r'c',s))   #match是开头开始匹配
        print(re.match(r'.*c', s))
    
    def re_match_object():
        # match对象
        s1 = 'Joey Huang'
        m = re.match(r'(w+) (w+)',s1)
        print(m.group(0,1,2))
        print(m.groups())
    
        m1 = re.match(r'w+ (w+)', s1)
        print(m1.group(1))
        print(m1.groups())
    
    
    if __name__ == '__main__':
        re_method()
        re_match_object()

    re.split()  

    split能够将匹配的子串分割后返回列表

    import re
    
    def re_method1():
        # split
        s = 'This is Joey Huang'
        print(re.split(r'W', s))
    
    if __name__ == '__main__':
        re.method1()
    

    re.sub()、re.subn()  

    sub能将匹配到的字段用另一个字符串替换返回替换后的字符串,subn还返回替换的次数

    import re
    
    def re_method2():
        # sub
        s2 = 'The first price is $9.90 and the second price is $100'
        print(re.sub(r'd+.?d*','<number>',s2,2)) # 还能指定替换的次数
    
    def re_method3():
        # subn
        s2 = 'The first price is $9.90 and the second price is $100'
        print(re.subn(r'd+.?d*','<price>',s2))
    
    if __name__ == '__main__':
        re_method2()
        re_method3()
    

    三、re的flags标识位

    re.DOTALL

    # 正则表达式默认以单行开始匹配的
    import re
    
    def re_pattern_syntax():
        # .表示任意单一字符
        # *表示前一个字符出现>=0次
        # re.DOTALL就可以匹配换行符
    ,默认是以行来匹配的
        print(re.match(r'.*', 'abc
    edf').group())
        print('*' * 80)
        print(re.match(r'.*', 'abc
    edf',re.DOTALL).group())
    
    if __name__ == '__main__':
        re_pattern_syntax()
    

    re.MULTILINE  

    # 正则表达式默认以单行开始匹配的
    import re
    
    def re_pattern_syntax1():
        # ^表示字符串开头(单行)
        # re.MULTILINE多行匹配字符串开头
        print(re.findall(r'^abc', 'abc
    edf'))
        print('*' * 80)
        print(re.findall(r'^abc', 'abc
    abc',re.MULTILINE))
    
    def re_pattern_syntax2():
        # $表示字符串结尾
        # re.MULTILINE表示行的结束
        print(re.findall(r'abcd$', 'abc1
    abc2'))
        print('*' * 80)
        print(re.findall(r'abcd$', 'abc1
    abc2',re.MULTILINE))
    
    if __name__ == '__main__':
        re_pattern_syntax1()
        re_pattern_syntax2()
    

    ?非贪婪模式  

    import re
    
    def re_pattern_syntax4():
        # greedy贪婪/non-greedy非贪婪,默认的是贪婪的匹配
        s = '<H1>title</H1>'
        print(re.match(r'<.+>', s).group())  #贪婪模式会匹配尽量多的匹配
        print(re.match(r'<.+?>', s).group()) #非贪婪模式匹配尽量少的匹配
        print(re.match(r'<(.+)>', s).group(1))
        print(re.match(r'<(.+?)>', s).group(1))
    
    def re_pattern_syntax5():
        # {m}/{m,}/{m,n}
        print(re.match(r'ab{2,4}', 'abbbbbbb').group())  #贪婪模式尽量匹配多
        print(re.match(r'ab{2,4}?', 'abbbbbbb').group()) #非贪婪模式尽量匹配少
        print('*' * 80)
    
    if __name__ == '__main__':
        re_pattern_syntax4()
        re_pattern_syntax5()
    

    re.I/re.IGNORECASE

    import re
    
    def re_pattern_flags():
        # re.I/re.IGNORECASE
        print(re.match(r'(Name)s*:s*(w+)','NAME : Joey',re.IGNORECASE).groups())
        print('*' * 80)
    
    if __name__ == '__main__':
        re_pattern_syntax_meta_char()
    

    re.VERBOSE

    import re
    
    def re_pattern_flags1():
        # re.VERBOSE此标识位可以添加注释/re.compile
        s = 'the number is 20.5'
        r = re.compile(r'''
                        d+   # 整数部分
                        .?   # 小数点,可能包含也可能不包含
                        d*   # 小数部分,可选
                        ''',re.VERBOSE)
        print(re.search(r,s).group())
        print(r.search(s).group())
        print('*' * 80)
    
    if __name__ == '__main__':
        re_pattern_syntax_meta_char1()

    四、原生字符串、编译、分组

    1、原生字符串

      细心的人会发现,我每一次在写匹配规则的话,都在前面加了一个r,为什么要这样写,下面从代码上来说明,

    import re
    #“”在ASCII 字符中代表退格键,”在正则表达式中代表“匹配一个单词边界”
    print(re.findall("blow","jason blow cat"))    #这里代表退格键,所以没有匹配到
     
    print(re.findall("\bblow","jason blow cat"))   #用转义后这里就匹配到了 ['blow']
     
    print(re.findall(r"blow","jason blow cat"))   #用原生字符串后就不需要转义了 ['blow']
    

    你可能注意到我们在正则表达式里使用“d”,没用原始字符串,也没出现什么问题。那是因为ASCII 里没有对应的特殊字符,所以正则表达式编译器能够知道你指的是一个十进制数字。但是我们写代码本着严谨简单的原理,最好是都写成原生字符串的格式。  

    2、编译

    如果一个匹配规则,我们要使用多次,我们就可以先将其编译,以后就不用每次都在去写匹配规则,下面来看一下用法

    import re
    
    def re_pattern_flags1():
        # re.VERBOSE此标识位可以添加注释/re.compile
        s = 'the number is 20.5'
        r = re.compile(r'''
                        d+   # 整数部分
                        .?   # 小数点,可能包含也可能不包含
                        d*   # 小数部分,可选
                        ''',re.VERBOSE)
        print(re.search(r,s).group())
        print(r.search(s).group())
        print('*' * 80)
    
    if __name__ == '__main__':
        re_pattern_syntax_meta_char1()
    

    3、分组  

      除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组,可以有多个组,分组的用法很多,

    记住正则分组: 去已经匹配到的数据中提取数据

    re.match()有无分组比较

    # 无分组
    r = re.match("hw+", origin)
    print(r.group())     # 获取匹配到的所有结果
    print(r.groups())    # 获取模型中匹配到的分组结果
    print(r.groupdict()) # 获取模型中匹配到的分组结果
    
    # 有分组
    # 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
    
    r = re.match("h(w+).*(?P<name>d)$", origin)
    print(r.group())     # 获取匹配到的所有结果
    print(r.groups())    # 获取模型中匹配到的分组结果
    print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
    

    re.search()有无分组比较  

    # 无分组
    
    r = re.search("aw+", origin)
    print(r.group())     # 获取匹配到的所有结果
    print(r.groups())    # 获取模型中匹配到的分组结果
    print(r.groupdict()) # 获取模型中匹配到的分组结果
    
    # 有分组
    
    r = re.search("a(w+).*(?P<name>d)$", origin)
    print(r.group())     # 获取匹配到的所有结果
    print(r.groups())    # 获取模型中匹配到的分组结果
    print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
    

    re.findall()有无分组比较  

    # 无分组
    r = re.findall("aw+",origin)
    print(r)
    
    # 有分组
    origin = "hello alex bcd abcd lge acd 19"
    r = re.findall("a((w*)c)(d)", origin)
    print(r)
    

    re.split()有无分组比较

    # 无分组
    origin = "hello alex bcd alex lge alex acd 19"
    r = re.split("alex", origin, 1)
    print(r)
    
    # 有分组
    
    origin = "hello alex bcd alex lge alex acd 19"
    r1 = re.split("(alex)", origin, 1)
    print(r1)
    r2 = re.split("(al(ex))", origin, 1)
    print(r2)
    

    再来一些例子:

    import re
    print(re.findall(r'(d+)-([a-z])','34324-dfsdfs777-hhh'))        # [('34324', 'd'), ('777', 'h')]
     
    print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(0))    # 34324-d 返回整体
    print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(1))    # 34324 获取第一个组
    print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(2))    # d 获取第二个组
    print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(3))    # IndexError: no such group
     
    print(re.search(r"(jason)kk1","xjasonkkjason").group())         #1表示应用编号为1的组 jasonkkjason
     
    print(re.search(r'(d)gg1','2j333gg3jjj8').group())                 # 3gg3 1表示使用第一个组d
     
    # 下面的返回None 为什么是空?而匹配不到3gg7,因为1的不仅表示第一组,而且匹配到的内容也要和第一组匹配到的内容相同,第一组匹配到3,第二组匹配到7 不相同所以返回空
    print(re.search(r'(d)gg1','2j333gg7jjj8'))
     
    print(re.search(r'(?P<first>d)abc(?P=first)','1abc1'))              # 1abc1 声明一个组名,使用祖名引用一个组 
     
    r=re.match('(?P<n1>h)(?P<n2>w+)','hello,hi,help')  # 组名的另外一种用法
    print(r.group())                                # hello 返回匹配到的值
    print(r.groups())                               # ('h', 'ello')返回匹配到的分组
    print(r.groupdict())                            # {'n2': 'ello', 'n1': 'h'} 返回分组的结果,并且和相应的组名组成一个字典
     
    # 分组是从已经匹配到的里面去取值
    origin ="hello alex,acd,alex"
    print(re.findall(r'(a)(w+)(x)',origin))                  # [('a', 'le', 'x'), ('a', 'le', 'x')]
    print(re.findall(r'aw+',origin))                         # ['alex', 'acd', 'alex']
    print(re.findall(r'a(w+)',origin))                       # ['lex', 'cd', 'lex']
    print(re.findall(r'(aw+)',origin))                       # ['alex', 'acd', 'alex']
    print(re.findall(r'(a)(w+(e))(x)',origin))               # [('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')]
     
    r=re.finditer(r'(a)(w+(e))(?P<name>x)',origin)
    for i in r :
        print(i,i.group(),i.groupdict())
    '''
        [('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')]
        <_sre.SRE_Match object; span=(6, 10), match='alex'> alex {'name': 'x'}
        <_sre.SRE_Match object; span=(15, 19), match='alex'> alex {'name': 'x'}
    '''
     
    print(re.findall('(w)*','alex'))                   # 匹配到了alex、但是4次只取最后一次即 x 真实括号只有1个
    print(re.findall(r'(w)(w)(w)(w)','alex'))       # [('a', 'l', 'e', 'x')]  括号出现了4次,所以4个值都取到了
     
    origin='hello alex sss hhh kkk'
    print(re.split(r'a(w+)',origin))                   # ['hello ', 'lex', ' sss hhh kkk']
    print(re.split(r'aw+',origin))                     # ['hello ', ' sss hhh kkk']
    

    练习  

    检测一个IP地址,比如说192.168.1.1,下面看下代码怎么实现的

    c=re.compile(r'((1dd|2[0-4]d|25[0-5]|[1-9]d|d).){3}(1dd|2[0-4]d|25[0-5]|[1-9]d|d)')
    print(c.search('245.255.256.25asdsa10.11.244.10').group())          # 10.11.244.10  245.255.256.25不符合要求所以就没有匹配出来
    

    这里来解释下上面的匹配规则,先看 (1dd|2[0-4]d|25[0-5]|[1-9]d|d).),其中1dd表示匹配100-199的数字 | 代表或的意思,2[0-4]d代表匹配100-249,25[0-5]代表匹配250-255,[1-9]d|d)代表匹配10-99和0-9,.代表匹配一个点,{3}代表将前面的分组匹配3次,后面的一部分类似就不说明了。要匹配一个ip重要的是先理解ip每个字段的形式,然后再来写匹配规则。  

     

    常用正则表达式:

    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}$
    邮箱:
    [a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+
    

      

  • 相关阅读:
    eclipse maven 插件 安装 和 配置
    Windows下 maven3.0.4的安装步骤+maven配置本地仓库
    js中转换Date日期格式
    Java 集合类详解
    java的反射机制
    Java 单例模式详解
    Http协议与TCP协议简单理解
    初识JavaScript,Ajax,jQuery,并比较三者关系
    MyBatis
    MyEclipse、Eclipse优化设置
  • 原文地址:https://www.cnblogs.com/xiaozhiqi/p/5770993.html
Copyright © 2011-2022 走看看