程序员充电站编程小问题首页投稿(暂停使用,暂停投稿)

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

2016-05-03  本文已影响389人  北静王

文:郑元春

人生苦短,我用Python!

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

5.逻辑和分组

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

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

#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的。

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

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

比如 (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',)
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的时候一定要在前面先写好分组模式,后面才能引用

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'

总结

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

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

参考

上一篇 下一篇

猜你喜欢

热点阅读