04.编程学习--正则表达式(入门二)

作者: 北静王 | 来源:发表于2016-05-03 22:37 被阅读389次

    文:郑元春

    人生苦短,我用Python!

    接上篇《编程学习--正则表达式(入门一)》,今天把剩下的基础部分完成,然后开始讲解Python的re模块,有时间的话就讲解下这个模块的源码。分析源码会让理解更加的透彻

    5.逻辑和分组

    经过这两天的学习和写码测试,终于明白了分组和《入门一》讲的正则表达式不同了。前面讲的正则表达式都是全匹配的,结果会把所有的符合情况的字符串全部返回(输出),但是分组的话,他只会输出符合分组模式的字符串,组外的字符串(如果有的话)只是用来做匹配辅助的。

    刚开始一直在纠结为啥组外的字符串没有输出来,今天突然想通了,Python的分组就是为了过滤符合分组模式的字符串的,所以Python的分组模式虽然是将整个的模式匹配了一遍,但是最后只会输出分组内的内容,在文中我会告诉你们怎么输出所有的匹配字符串。(那个在线的正则匹配验证的网站中还是会输出辅助字符的)。

    • 5.1|
      这个就是‘或’功能了,很简单不解释。|的优先级比较低,所以 ab|cd能够匹配的模式是ab或者是cd.如果|是在分组内的话,那么分组就会改变他的优先级,它的范围就只能在分组内了。
    #case 1: 没有分组
    reStr="ab|cd"
    targetStr="ab cd abcd abc acd"
    re.findall(reStr,targetStr)
    >>>['ab','cd','ab','cd','ab','cd']
    
    #case 2:使用分组(下部分讲分组)
    reStr="a(b|c)d"
    targetStr="ab cd abcd abc acd"
    re.findall(reStr,targetStr)
    >>>['c']
    #先看下分组
    result=re.search(reStr,targetStr)  #注意这里使用的是search函数,不是finall函数
    result.groups()
    >>>('c',)
    result.span()
    >>>(15, 18)
    result.group(1)
    >>>'c'
    

    具体的分组下面会讲解,可以看到,虽然输出的仅仅是组内的匹配内容,但是通过span()还是可以看到其实是匹配了整个的acd的。

    • 5.2()

    括号括起来的表达式将作为分组.为什么要引入分组呢,主要的原因还是在数量词的组合运用上。

    有的时候模式后面会跟着数量词,而数量词只是匹配前一个字符,也就是现在只能重复单个字符,如果我们重复一个字符串怎么办呢,使用()分组可以将多个字符组合成一个字符串,之后就可以指定这个字符串的重复规则了。

    比如 (ab){2,3}就表示的是abab或者是ababab。分组是可以嵌套使用的,将正则表达式从左边到右边扫描,每个分组就是找到的左括号(的顺序,以为后面还有使用分组索引、分组别名来替换分组的使用方法,这里只是顺便一提。

    在这里先说下re模块中的两个函数:group和groups。具体的使用将会在re模块那一章节里面进行讲解。这里只是为了下面需要使用才进行简单的说明。

    group和groups是两个不同的函数。

    分组与不分组的情况区别是一个带着括号,另一个不带着括号,有的时候我们需要的是返回的字符串只匹配分组内的规则,有的时候我们需要返回的字符串是匹配整个正则表达式的规则,这两种需求都可以使用re.search()re.match()函数测试。两种函数都会返回一个matchobject对象,这个对象里面有如下的属性或者是方法:

    属性:

    1. string:

    匹配时使用的文本。

    1. re:

    匹配时使用的Pattern对象。

    1. pos:

    文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。

    1. endpos:

    文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。

    1. lastindex:

    最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。

    1. lastgroup:

    最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。

    方法:

    1. group([group1, …]):

    获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。

    1. groups([default]):

    以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。

    1. groupdict([default]):

    返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。

    1. start([group]):

    返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。

    1. end([group]):

    返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。

    1. span([group]):

    返回(start(group), end(group))。

    1. expand(template):

    将匹配到的分组代入template中然后返回。template中可以使用\id或\g<id>、\g<name>引用分组,但不能使用编号0。\id与\g<id>是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符'0',只能使用\g<1>0。

    由于我们的分组是可以嵌套使用的,所以每个分组从左到右的索引就是扫描顺序,通过group(n)的方式来访问匹配的分组的字符。

    而group() == group(0) == 所有匹配的字符,与括号无关,这个是API规定的。

    所以带有分组的会返回两种格式,一种是和不带分组()一样,返回的是匹配整个正则表达式的字符串,一种是只返回分组内的字符串。下面用代码说话。

    #case 1:最简单的分组
    reStr="a(bc)"
    targetStr="abcabc"
    # 使用findall方式
    re.findall(reStr,targetStr)
    >>>['bc', 'bc'] #返回所有的匹配到的组内字符串
    # 使用search方式
    result=re.search(reStr,targetStr)
    result.group()
    >>>'abc'  #分组外的字符a也输出了
    result.group(0)
    >>>'abc'
    result.group(1)
    >>>'bc'  #只输出分组中匹配的内容
    result.groups()
    >>>('bc',) #输出的是tuple
    
    #case 2:分组索引
    reStr="a(bc)(de)"
    targetStr="abcde bc de"
    re.findall(reStr,targetStr)
    >>>[('bc', 'de')]
    
    result=re.search(reStr,targetStr)
    result.group()
    >>>'abcde'
    result.group(0)
    >>>'abcde'
    result.group(1)
    >>>'bc'
    result.group(2)
    >>>'de'
    result.groups()
    >>>('bc', 'de')
    result.group(3)
    >>>IndexError: no such group
    result.group
    

    通过case 1和case 2的比较就可以明白分组的索引是怎么回事了吧。同时也能够明白group()``group(0)以及group(n)还有groups()的关系了吧。

    #case 3:分组嵌套
    reStr="a(b(cd))"
    targetStr="cd bcd abcd"
    re.findall(reStr,targetStr)
    >>>[('bcd', 'cd')]
    
    result=re.search(reStr,targetStr)
    result.group()
    >>>'abcd'
    result.group(0)
    >>>'abcd'
    result.group(1)
    >>>'bcd'
    result.group(2)
    >>>'cd'
    result.groups()
    >>>('bcd', 'cd')
    
    #case 4: 与数量模式并用
    reStr="(abc){1,2}"
    targetStr="abcabc"
    re.findall(reStr,targetStr)
    >>>['abc']
    
    result=re.search(reStr,targetStr)
    result.group()
    >>>'abcabc'
    result.group(0)
    >>>'abcabc'
    result.group(1)
    >>>'abc'
    result.groups()
    >>>('abc',)
    
    #case 5:与`|`的联合使用
    reStr="(ab|cd)"
    targetStr="abcd"
    result=re.search(reStr,targetStr)
    result.group()
    >>>'ab'
    result.group(1)
    >>>'ab'
    result.groups()
    >>>('ab',)
    
    • 5.3(\\<number>)
      分组的时候,每一对括号代表着一个分组,从左边到右边依次编号为1,2,3......。match或者是search函数返回的是有group信息,通过访问group(1),group(2)可以直接取得分组索引为1,2的分组。
    reStr=r"(ab)(cd)\\1"  #注意前面的r,以后还是全部带r吧
    targetStr="abcdab"
    re.findall(reStr,targetStr)
    >>>[('ab', 'cd')]
    result=re.search(reStr,targetStr)
    result.group()
    >>>'ababab'
    result.group(1)
    >>>'ab'
    #如果将正则模式换成
    reStr=r"(ab)(cd)\\1\\2"
    targetStr="abcdabcd"
    result=re.search(reStr,targetStr)
    result.group()
    >>>'abcdabcd'
    result.group(1)
    >>>'ab'
    result.group(2)
    >>>'cd'
    

    注意:使用index的时候一定要在前面先写好分组模式,后面才能引用

    • 5.4(?P<name>)
      上面说的时候有没有点匿名函数的意思啊,其实还可以使用命名的方式来引用前面的分组信息。
    reStr=r"(?P<g1>ab)(?P=g1)"
    targetStr="abab"
    result=re.search(reStr,targetStr)
    result.group()
    >>>'abab'
    result.group(1)
    >>>'ab'
    result.groupdict()
    >>>{'g1': 'ab'}
    result.group('g1')
    >>>'ab'
    

    当使用name的时候就可以使用group(name)的方式来访问了

    reStr=r"(?P<g1>ab)c(?P<g2>de)(?P=g1)(?P=g2)"
    targetStr="abcdeabde"
    result=re.search(reStr,targetStr)
    result.group()
    >>>'abcdeabde'
    
    • 5.5(?P=name)
      上面在5.4中已经说明了name的使用方法了。

    总结

    分组还是比较好的一个功能的,做过Django项目的同学一定在url配置的时候使用过正则表达式,所以url在Django叫做url pattern

    同时需要注意group()和group(n)以及groups()的用法!另外,可以使用name来标识分组也是很人性化的。

    参考

    相关文章

      网友评论

        本文标题:04.编程学习--正则表达式(入门二)

        本文链接:https://www.haomeiwen.com/subject/xfrfrttx.html