这一篇主要是上一篇没提到的一些知识点的补充说明:
1、贪婪匹配:
上一篇已经介绍过正则表达式中的闭包操作符基础,当模式匹配使用这些操作符时,它将试图尽可能多的匹配字符,这通常被叫做贪婪匹配。怎么理解呢?举个例子看一看:
import re
s = '<h1>Hello,World.</h1>'
print(re.search('<.*>',s).group())
#<h1>Hello,World.</h1>
可以看到它把这段字符串全部都输出了,也即全部被匹配了,它没有在匹配到<h1>后就适可而止,
而是在探索是否可能匹配到更多的字符,这就是贪婪匹配。
那么贪婪匹配可以被抑制吗?答案是肯定的;
通过在*、+等可以造成贪婪匹配的操作符后添加?可以抑制贪婪匹配,例子:
s = '<h1>Hello,World.</h1>'
print(re.search('<.*?>',s).group())
#<h1>
?会使匹配模式机制"偷懒",即如果可能,将尽可能少的匹配,这也叫惰性匹配
2、分组的副作用
我们可以使用圆括号进行分组,这虽然可以给我们提供诸多便利,但它存在一个副作用,那就是:匹配模式的子字符串会被缓存下来,缓存的字符串可以供后续使用,但如果我们仅仅只是需要用一次那么额外的缓存将浪费我们的内存空间,这时可以用(?:)来消除缓存,(?:)也叫非捕获元字符,关于它将在稍后介绍;
3.捕获组与非捕获组
捕获组:
把正则表达式或它的子式用一对圆括号包裹起来,这就构成了一个捕获组,捕获组中的数据将被存储在临时缓冲区中,可以用group(x)访问,示例:
s = 'abc@123.ddt'
m = re.search('(a\w+@)(\d+\.)(\w+)',s))
print(m.group(2))#123.
非捕获组:
非捕获组包裹的内容不会被存进缓冲区,当然也没有编号,一个最简单的非捕获组例子是 (?: ),即上文提到的非捕获元,用它来重写捕获组,使其成为非捕获组;
需要格外强调的是,非捕获组虽然不参与编号分配,但它参与匹配;
示例:
s = 'abc@123.ddt'
m = re.search('(?:a\w+@)(?:\d+\.)(?:\w+)',s)
print(m.group(2))
#此时运行程序,将得到IndexError错误,提示 no such group
4.findall()相关说明
findall()函数匹配字符串中的所有字符,并返回一个列表,这是上一篇文章对findall()用法的介绍,这里再补充一点,当匹配模式有分组的情况下,即存在捕获组,findall()将只会匹配捕获组中的内容;
示例:
s = 'abc@123.ddt'
print(re.findall('a\w+@(\d+\.)\w+',s))
#['123.']
可以看到只有中间捕获组的内容被打印出来,这可以帮助我们在模式匹配时去掉垃圾数据
如果你想要让它捕获全部信息,可以把捕获组设置为非捕获组,即前面提到的 ?:
s = 'abc@123.ddt'
print(re.findall('a\w+@(?:\d+\.)\w+',s))
#['abc@123.ddt']
5.一些非捕获元字符
- (?:pattern) :参与匹配但不获取匹配结果,即非获取匹配;
- (?=pattern) :正向肯定预查,非获取匹配。例(?=.com)表示只有一个字符串后面是".com"才会进行匹配;
- (?!pattern) :正向否定预查,参照(?=pattern);
- (?<=pattern):反向肯定预查。例(?<=25a|88d)表示只有一个字符串前面是25a或者88d才会进行匹配;
- (?<!pattern):反向否定预查,参照(?<=pattern);
- (?#pattern) :此处不做匹配,只作为注释;
对于(?#pattern)的一个例子:
s = 'abc@123.ddt'
print(re.findall(r'(?#a\w+@)\d+\.\w+',s))
#['123.ddt']