Javascript正则表达式中被动(非捕获)组的目的是什么?

时间:2013-09-02 18:07:36

标签: javascript regex

Javascript正则表达式中被动组的目的是什么?

被动组以问号冒号开头:(?:group)

换句话说,这两件事看起来是一样的:

"hello world".match(/hello (?:world)/)
"hello world".match(/hello world/)

在什么情况下你需要非捕获组?为什么?

4 个答案:

答案 0 :(得分:16)

捕获

的两个用例

正则表达式中的捕获组实际上有两个不同的目标(正如名称“捕获组”本身所暗示的那样):

  1. 分组 - 如果您需要将组视为单个实体,以便将某些内容应用于整个组。

    可能最简单的例子是包括一个可选的字符序列,例如“foo”可选地后跟“bar”,正则表达式:/foo(bar)?/(捕获组)或/foo(?:bar)?/(非捕获组)。请注意,尾随?将应用于整个组(bar)(在本例中由一个简单的字符序列bar组成)。如果您只想检查输入是否与正则表达式匹配,那么无关紧要您是使用捕获还是非捕获组 - 它们的行为相同(除非非捕获)组是slightly faster)。

  2. 捕获 - 如果您需要提取输入的部分

    例如,你想从“农场包含8头奶牛和89只兔子”这样的输入中获得兔子的数量 (英语不是很好,我知道) 。正则表达式可能是/(\d+)\s*rabbits\b/。成功匹配后,您可以获取捕获组 from JavaScript code(或任何其他编程语言)匹配的值。

    在此示例中,您有一个捕获组,因此您可以通过索引0访问它(有关详细信息,请参阅this answer)。

    现在假设你想确保“地方”被称为“farm”“ranch”。如果不是这样,那么你不想提取兔子的数量(用正则表达式 - 你不希望正则表达式匹配)。

    所以你重写你的正则表达式如下:/(farm|ranch).*\b(\d+)\s*rabbits\b/。正则表达式本身就可以工作,但是你的JavaScript被破坏了 - 现在有两个捕获组,你必须更改你的代码才能得到第二个捕获组的内容兔子(即改变指数从0到1)。第一组现在包含字符串“farm”或“ranch”,您不打算提取它。

    一个非捕获组来救援:/(?:farm|ranch).*\b(\d+)\s*rabbits\b/。它仍匹配“farm”或“ranch”,但不捕获它,因此不会移动后续捕获组的索引。并且您的JavaScript代码可以正常工作而无需更改


  3. 示例可能过于简单,但考虑到您有一个非常复杂的正则表达式,包含许多组,并且您只想捕获其中的少数几个。非捕获组非常有用 - 您不必计算所有组(仅捕获组)。

    此外,非捕获组用于文档目的:对于读取代码的人,非捕获组表示您对提取内容不感兴趣,您只想确保它匹配。


    关于关注点分离

    的几句话

    捕获群组打破SoC principle 的典型示例。此语法构造提供两个不同的目的。随着问题变得明显,附加构造?:)被引入禁用两个功能之一

    这只是设计错误。也许缺乏“免费特殊角色”扮演了它的角色......但它仍然是一个糟糕的设计。

    正则表达式是一个非常古老,强大且广泛使用的概念。出于向后兼容性的原因,现在不太可能修复这个漏洞。这只是关注点分离的重要性的一课。

答案 1 :(得分:9)

非捕获与“普通”(捕获)组只有一个区别:它们不需要正则表达式引擎记住它们匹配的内容。

用例是有时你必须(或应该)使用一个组,不是因为你对它所捕获的内容感兴趣而是出于语法原因。在这些情况下,使用非捕获组而不是“标准”捕获组是有意义的,因为它不太占用资源 - 但如果您不关心它,捕获组将以完全相同的方式运行。

您的具体示例并不能很好地使用非捕获组,因为这两个表达式完全相同。一个更好的例子可能是:

input.match(/hello (?:world|there)/)

答案 2 :(得分:3)

如果要将修改器应用于组。

/hello (?:world)?/
/hello (?:world)*/
/hello (?:world)+/
/hello (?:world){3,6}/

答案 3 :(得分:3)

在需要条件时使用它们,而不关心哪个选项会导致匹配。

非捕获组可以简化匹配复杂表达式的结果。这里,组1始终是名称发言者。如果没有非捕获组,说话者的姓名可能会在第1组或第2组中结束。

/hello (?:world|foobar )?said (.+)/