正则表达式-非捕获组的迷思 ( non-capturing group ) (?:)

内容纲要

(?:…)
正则括号的非捕获版本。 匹配在括号内的任何正则表达式,但该分组所匹配的子字符串不能在执行匹配后被获取或是之后在模式中被引用。

这解释真叫人头大。

例一

(aa|bb) 匹配 aabb

结果是

类型 范围 内容
Match 1
Full match 0-2 aa
Group 1. 0-2 aa
Match 2
Full match 2-4 bb
Group 1. 2-4 bb

例二

(aa|bb)+ 匹配 aabb

结果是

类型 范围 内容
Match 1
Full match 0-4 aabb
Group 1. 2-4 bb

第一个先匹配到 aa ,再匹配到 bb 。第二个直接匹配了 aabb 。
第一个和第二个的差别在于,用 + 重复匹配组,后果就是组只会保留最后一个迭代。

例三

我们再试试:

(?:aa|bb)+ 匹配 aabb

类型 范围 内容
Match 1
Full match 0-4 aabb

组保存的消失了?这就是非捕获组的用处,意思就是不保存组的内容到内存中,也就不会标号,于是只会得到匹配。

例四

这个用处是什么呢
先看看这个 Python 代码:

reg = r"(0\d{2})-(\d{7})"

s = "010-3838438"

re.findall(reg, s)

#  Out:[('010', '3838438')]

reg = r"(?:0\d{2})-(?:\d{7})"

s = "010-3838438"

re.findall(reg, s)

#  Out:['010-3838438']

上述差别如何发生?

第一个匹配的结果是

类型 范围 内容
Full match 0-11 010-3838438
Group 1. 0-3 010
Group 2. 4-11 3838438

而第二个匹配的结果是

类型 范围 内容
Full match 0-11 010-3838438

Python 自带的 re 模块函数:

re.findall
对 string 返回一个不重复的 pattern 的匹配列表, string 从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。

也就是从左到右,按正则表达式,不重复地匹配字符串,返回一个字符串列表。 如果正则表达式有一个或更多的组,返回组的列表,优先返回组。

第一个匹配里有两个组,所以只返回了组。

不得不说这个函数实现的很失败,为什么不把所有结果都返回?

最后一个例子

建议你自己猜一下输出结果:

s = 'exp=50;exp=51;'

re.findall(r'exp=(50|51);', s)

re.findall(r'exp=(?:50|51);', s)




#  Out:['50', '51']

#  Out:['exp=50;', 'exp=51;']

为什么出现这种差别?

前者有两个匹配,每个匹配有一个组。后者只有两个匹配。

因为 findall 总是尽力返回捕获的组,也就是括号内的字符串。(太蠢了)

使用了 (?:) ,它不会保存匹配的组。如果 findall 没有捕获到组,就会返回所有匹配的字符串。

发表评论

电子邮件地址不会被公开。 必填项已用*标注