【正则表达式】非捕获组 (?: ... )的使用方法和必要性
定义
非捕获组,用 (?: ... )
表示。这意味着它将匹配括号内的内容,但是不会将匹配的内容存储到内存中供后续引用。
这对于那些我们想要作为一个整体处理,但是又不需要单独捕获其内容的情况非常有用。
举例
假设我们有一个字符串 abc123def,并且我们想要匹配其中的数字,同时忽略其他部分。如果我们使用以下正则表达式:
(a)(bc)(123)(def)
这个表达式会捕获四个不同的组:a
,bc
,123
和 def
。如果我们只是关心中间的数字 123
而不是其他部分,那么我们可以把不需要的部分放入非捕获组中:
(?:a)(?:bc)(123)(?:def)
这样只有数字 123
会被捕获。如果我们使用这个正则表达式去查找匹配项,只有 123
会被返回为捕获组。
与普通捕获组相比
当你使用普通捕获组 (...) 时,正则表达式引擎会自动存储所有捕获组的结果。如果正则表达式中有大量的捕获组,即使你并不需要所有这些结果,也会占用额外的内存和计算资源。
使用非捕获组 (?: ... ) 可以避免这种不必要的开销,提高性能。具体来说:
-
内存节省:
- 正则表达式引擎需要为每个捕获组分配内存空间来存储匹配的结果。
- 使用非捕获组
(?: ... )
可以避免这部分内存分配,从而节省内存。
-
性能优化:
- 捕获组的创建和管理涉及到额外的操作,包括存储和检索匹配结果。
- 使用非捕获组可以减少这些操作,从而提高正则表达式的执行速度。
使用场景
第一次接触到这个概念的人可能会想:不需要处理那不要用()捕获不就行了吗。
但是使用非捕获组有一些好处。
首先,非捕获组是一个捕获组加上“?:”,那它首先有一些普通捕获组有的好处:
- 分组匹配:括号可以用来确保某些部分作为一个整体进行匹配。
- 优先级控制:括号可以用来控制匹配的优先级,特别是当有嵌套的模式时。
- 结构清晰性:括号可以使复杂的正则表达式更具可读性和可维护性。
- 重复利用:有时你可能需要在同一个表达式中多次使用相同的模式,括号可以帮助你实现这一点。
如果说2、3、4点都是一些非必要的使用情况,那么将某部分作为一个整体进行匹配在一些情况下可能是必须的。
比如:需求是匹配一行参数文本,捕获其出现的参数,但其中有些参数可能不存在。
{ stream: "data_stream", pcp: 1, interface: "eth0" } { vlan: 10, stream: "data_stream", pcp: 1, interface: "eth0" }
比如上面这两行文本,第一行就比第二行少了vlan这个参数。
这时候,先将匹配 vlan: 10 的这部分使用括号写成一个整体,然后在括号后面跟上一个问号,就可以表示这个整体模式可以存在也可以不存在:
(?:\s*vlan:\s*(?<VID>\d+),?)?
这个表达式表示:
- 整个模式 \s*vlan:\s*(?<VID>\d+),? 是可选的。
- 如果这个模式存在,它将匹配 vlan: 后面跟着一个或多个空白字符、一个或多个数字(这些数字被捕获为 VID)、一个可选的逗号。
总结一下:
如果只在这里使用普通捕获组:
- 将这段内容作为一个整体,加上?即可表示个整体模式可以存在也可以不存在
- 结构清晰明了
在非捕获组内设置了普通捕获组的写法,相比起普通捕获组:
- 非捕获组减少了不必要的内存分配和正则表达式引擎的额外操作
- 捕获的内容更精确,简化后续的逻辑处理
这样看来,这就是比较合理、高效的一种写法。