正则表达式

定义:
    是一个特殊的字符序列,一个字符串是否符与此字符序列相匹配
作用:
    可以快速检索文本、实现一些替换文本的操作
场景:
    检查一串数字是否是电话号码
    检查字符串是否是Email格式
    把文本中指定单词替换为其他单词

示例:
    判断下列字符串是否包含Python
        a = 'C|C++|Java|C#|Python|JavaScript'

        print(a.index('Python')>-1)
        # True
        print('Python' in a)
        # True

    上述是使用python内置的函数来实现字符串查找等功能

    使用常量正则表达式:

        使用re的方法: re.findall('正则表达式','所匹配的字符串')
            import re
            a = 'C|C++|Java|C#|Python|JavaScript'
            print(re.findall('Python',a))
            #输出:['Python']
            print(re.findall('C',a))
            #输出:['C', 'C', 'C']
            #判断是否包含字符串:
            print( len(re.findall('Python',a)) > 0 ) #输出True

        常量正则表达式:规则单一,没有体现出正则的强大
        正则的强大之处就在于构造有意义的规则

    构造有规则的正则表达式:
        示例:
            提取字符串中的所有数字
            方法一:循环遍历每个字符
            方法二:构造正则,使用findAll方法 :'\d'表示0~9
            'Python'    -   普通字符
            '\d'        -   元字符之一
            '\D'        -   去掉数字    
            代码:
                import re
                a = 'C1|C++2|Java3|C#4|Python5|JavaScript6'
                print(re.findall('\d',a))
                #['1', '2', '3', '4', '5', '6'] 找出了全部数字
                print(re.findall('\D',a))   去掉数字
                #['C', '|', 'C', '+', '+', '|', 'J', 'a', 'v', 'a', '|', 'C', '#', '|',
                # 'P', 'y', 't', 'h', 'o', 'n', '|', 'J', 'a', 'v', 'a', 'S', 'c', 'r',
                # 'i', 'p', 't']

        元字符很多,不需要记忆元字符,要学会自由组合
        更重要的是:模式    -   任意的、A或B、等等

字符集

形式:
    中括号 [xyz]:x或y或z
示例:
    找出中间一个字符是c或者是f的字符串
代码:
    import re
    s = 'abc,acc,adc,aec,afc,apc'
    print( re.findall( 'a[cf]c',s ) )   #['acc', 'afc']
    #输出:       a       [cf]        c
    #       借助a定界   表示a或f   借助c定界
    #       普通字符    元字符     普通字符

    中括号 [^xyz]:除去x或y或z
    代码:
        print( re.findall( 'a[^cf]c',s ) )  #['abc', 'adc', 'aec', 'apc']
    中括号 [a-d]:a或b或c或d
    代码:
        print( re.findall( 'a[a-c]c',s ) )  #['abc', 'acc']

概括字符集

形式:
    类似'\d',表示囊括所有的数字,
    其他相同作用的规则:  
        '\d' = 字符集'[0-9]'
        '\D' = 字符集'[^0-9]'
场景:
    匹配字符串中的各类字符
示例:

    import re

    a = 'C1|C++2|Java3( C#4\n)Python5_JavaScript6'

    print( re.findall( '\w',a ) )   #返回单词字符,字母,数字,和下划线
    #['C', '1', 'C', '2', 'J', 'a', 'v', 'a', '3', 'C', '4', 'P', 'y', 't',
    # 'h', 'o', 'n', '5', '_', 'J', 'a', 'v', 'a', 'S', 'c', 'r', 'i', 'p',
    # 't', '6']
    print( re.findall( '\W',a ) )   #返回非单词字符
    #['|', '+', '+', '|', '(', ' ', '#', '\n', ')']

    使用中括号的形式匹配字母和数字
        print( re.findall( '[0-9a-zA-Z]',a ) )



    print( re.findall( '\s',a ) )   '\s'    匹配空白字符
    #[' ', '\n']
    print( re.findall( '\S',a ) )   '\S'    匹配非空白字符

    print( re.findall( '.',a ) )       '.'        匹配除\n之外的字符

第一种数量词

形式:
    {N} 或者 {M,N}

示例:
    import re

    a = 'tom12,gimmy*77,kit_001,jack,yang'

    print(re.findall('[a-z][a-z][a-z]',a))
    #输出['tom', 'gim', 'kit', 'jac', 'yan']
    这里重复使用三次中括号字符集完成了匹配长度为三的字符

    print(re.findall('[a-z]{3}',a))
    #输出['tom', 'gim', 'kit', 'jac', 'yan']
    上述使用大括号:{},使用数量词来完成重复,结果与上述一致

    print(re.findall('[a-z]{3,6}',a))
    #输出['tom', 'gimmy', 'kit', 'jack', 'yang']
    如果需要匹配出全部英文单词,即匹配出长度为3-5的字符,可以使用{x,y}来限定重复次数
    此处的匹配是:贪婪的,检测到gim之后,并不停止,因为没有达到最大限定5,故还会继续匹配,一直到匹配到gimmy之后才停止
    python默认是贪婪的匹配

    非贪婪的模式匹配正则:{}?
    print(re.findall('[a-z]{3,6}?',a))
    #输出['tom', 'gim', 'kit', 'jac', 'yan'],此处匹配到3字符后就停止了

第二种数量词

形式:
    *, +, ?
含义:
    *   匹配*号前的字符0次或者无限多次
    +   匹配+号前的字符1次或者无限多次
    ?   匹配?号前的字符0次或1次
示例:
    import re

    a = 'to_tom12tomy*77tomyy,kit_001,jack,yang'

    print(re.findall('tomy*',a))
    #输出['tom', 'tomy', 'tomyy']
    解释:
       tom     成功匹配t,o,m,匹配y不成功,即成功0次
       tomy    成功匹配t,o,m,y
       tomyy   成功匹配t,o,m,y,y又匹配成功,因为可以无限次,故含有y
    print(re.findall('tomy+',a))
    #输出['tomy', 'tomyy']
    解释:
       to      没出来,因为to之后的my没匹配成功,即成功-1次
       tom     没出来,因为tom之后的y没匹配成功,即成功0次
       tomy    成功匹配t,o,m,y
       tomyy   成功匹配t,o,m,y后,后续匹配y,因为y在tomy内,故成功
    print(re.findall('tomy?',a))
    #输出['tom', 'tomy', 'tomy']
    解释:
       to      没匹配出来,因为匹配失败2次,即匹配成功-1次
       tom     匹配出tom,因为匹配到tom之后再匹配'y'没有匹配到,即匹配成功0次
       tomy    成功匹配t,o,m,y
       tomy    注意tomyy匹配为tomy,因为成功匹配t,o,m,y后,y虽又匹配成功,但已经是第二次,故丢弃

    print(re.findall('tomy{1,2}',a))    #使用{}作为数量词限定
    #输出['tomy', 'tomyy']
    print(re.findall('tomy{1,2}?',a))   #非贪婪
    #输出['tomy', 'tomy']

边界匹配符

形式:
    ^ 、 $
场景:
    验证字符串长度:4-8位
示例:
    import re

    a = '11345610000'

    print(re.findall('\d{4,8}',a)) 
    #输出['12345610']
    这样可以匹配出4-8位字符,但是判断不了原串是否为4-8位

    此处应该使用面向字符串的匹配:
        边界匹配:^表达式$,将‘表达式’作为整体匹配
        示例:
            print(re.findall('^\d{4,8}$',a))
            #输出[]    

    边界符的作用:

        不用边界符时
        print(re.findall('00',a))
        #输出['00', '00']

        使用边界附    ^   从字符串开始的位置开始匹配
        print(re.findall('^00',a))
        #输出[]
        因为字符串开头为1,故匹配失败


        使用边界附    $   从字符串末尾的位置开始匹配
        print(re.findall('00$',a))
        #输出['00']
        字符串结尾为0000,这里只匹配到0,0就结束

形式:
    ()
场景:
    判断字符串是否包含连续!N个子串
示例:
    例如判断串中是否存在三个'to'
代码:
    import re

    a = 'to_tom12tomy*77tomyy, kit_001, jack, yang_tototo'

    print(len(re.findall('(to){3}',a))>0)
    #输出True   表示存在tototo,连续匹配 t且o 三次

    中括号[]和小括号()的区别:
        [xyz]:x或y或z
        (xyz):x且y且z

匹配模式

形式:
    re.I  re.S
示例:
    import re

    a = 'to_tom\ntomy*77tomyy, kit_001, jack, yang_tototo'

    print(re.findall('Tom',a))
    #[] 匹配不到tom,大小写不符

    使用模式:添加第三个参数:
    print(re.findall('Tom', a, re.I ))
    #['tom', 'tom', 'tom']

    如何匹配'tom\n'  ?
        print(re.findall('Tom.', a, re.I ))
        #['tomy', 'tomy']   没匹配出来

        使用添加模式re.S
        print(re.findall('Tom.', a, re.I | re.S ))
        #['tom\n', 'tomy', 'tomy']
        这里由于匹配了模式re.S,所以匹配出了\n字符

sub()

形式:    
    re.sub(表达式,替换成,原串))
场景:
    替换 字符串
示例:

    简单替换(与内置方法replace类似)
        import re

        a = 'to_tomtomy*77tomyy, kit_001'

        print(re.sub('tom','Tom',a))
        # to_TomTomy*77Tomyy, kit_001

        print(re.sub('tom','Tom',a, 1))
        #to_Tomtomy*77tomyy, kit_001
        #注意:使用了count=1参数,只替换了第一个tom

        #简易替换:
        print(a.replace('tom','Tom', 1))
        #to_Tomtomy*77tomyy, kit_001
        #同样是替换了第一个tom

    高级替换:
        sub的强大之处,在于可以附加一个函数参数
        示例:
            def convert(value): #注意需要带一个参数
                print(value)
            print(re.sub('tom',convert,a))
            #输出
            #<_sre.SRE_Match object; span=(3, 6), match='tom'>
            #<_sre.SRE_Match object; span=(6, 9), match='tom'>
            #<_sre.SRE_Match object; span=(13, 16), match='tom'>
            #to_y*77yy, kit_001

            注意上述替换后结果中tom消失了,而且传入函数的参数为对象:
            三个对象:表示进行了三次字符串匹配
            span(3,6)表示第一次tom字符串的起始边界为3,结束后的边界为6


        通过函数进行动态替换,替换为return的值
        示例:
            def conver(value): #注意需要带一个参数
                match = value.group()
                #value.group()  表示匹配成功时传进函数的字符串
                return '['+match+']'

            print(re.sub('tom',conver,a))
            #输出:to_[tom][tom]y*77[tom]yy, kit_001

    进一步复杂的替换:
        示例:
            替换所有大于等于50的数字为 [BIG] ,小于的剔除
        代码:

            import re

            a = 'to_to5mtomy*77tomy65y'

            def conver(value): #注意需要带一个参数    
                match = value.group()
                print(match)
                if( int(match) >= 50):   #注意转换类型
                    return ' [BIG] '
                return ''
            print(re.sub('\d+',conver,a))
            #输出:
            #5
            #77     注意,此处77作为整体传入,不会分两次传入7,7
            #65
            #to_tomtomy* [BIG] tomy [BIG] y ,完成替换

search(),match()

地位:
    这是re模块正则匹配的三种方法的除findall之外的其余两种 
形式:
    re.match
    re.search
示例:

    import re

    a = '1_to_to5mtomy*77tomy65y'

    print(re.match('\d',a)) 
    #输出<_sre.SRE_Match object; span=(0, 1), match='1'>
    match()特点:
        从字符串首字符开始匹配,如果失败,将返回None,object; span=(0, 1)其实就是'1'

    print(re.search('\d',a)) 
    #输出<_sre.SRE_Match object; span=(5, 6), match='5'>
    search()特点:
        将尝试搜索字符串,将找到的第一个字符返回,注意object; span=(5, 6),其实就是字符'5'

    对于返回的object,有相关操作方法
        group()方法:返回匹配的字符、字符串
        示例:
            print(re.search('\d',a).group()) #输出 1
        span()方法:返回匹配结果在原串中的位置
        示例:
            print(re.search('\d',a).span()) #输出 (0, 1)

    比较与findall方法,他们只要匹配成功就会停止匹配
    比较与findall方法,他们会返回对象
    print(re.findall('\d',a)) #输出['1', '5', '7', '7', '6', '5']

匹配返回对象的方法 分组:group()

场景:
    例如取出下面的时间信息
示例:
    import re

    a = '#2002 2017-10-30 11:12:34 #!'

    首先使用普通字符 定界,这里的边界是'2002 '和' #!'
    print(re.search('2002 \w #!',a).group())
    # 报错,因为seach无返回值,None.group()会报错

    print(re.search('2002 \w* #!',a).group())
    # 报错,因为seach无返回值,None.group()会报错

    print(re.search('2002 .* #!',a).group())
    # 输出:2002 2017-10-30 11:12:34 #! 
    这里并没有只返回中间内容,连边界也返回了

    print(re.search('2002 .+ #!',a).group())
    # 同样输出:2002 2017-10-30 11:12:34 #!

    带参数的group()
        注意:
            group(parameter): group() = group(0)
            group(0):永远记录完整匹配结果
            group(N):N>0,记录第N个分组
        示例:
            print(re.search('2002 (.+) #!',a).group(1))
            #输出:2017-10-30 11:12:34
        注意上述括号(),表示一个分组,使用group(1),表示返回第一个分组


    使用findall完全可以实现:
    print(re.findall('2002 (.+) #!',a))
    #输出:['2017-10-30 11:12:34']


    另:groups()
    print(re.search('2002 (.+) #!',a).groups())
    直接返回全部分组:('2017-10-30 11:12:34',)