题 什么是非捕获组? (?:)做什么?


怎么样 ?: 使用它有什么用?


1327
2017-08-18 13:17


起源


这个问题已被添加到 Stack Overflow正则表达式FAQ,在“群组”下。 - aliteralmind


答案:


让我试着用一个例子解释一下。

请考虑以下文本:

https://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex

现在,如果我在下面应用正则表达式...

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

...我会得到以下结果:

Match "https://stackoverflow.com/"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"

但我不关心协议 - 我只想要URL的主机和路径。因此,我将正则表达式更改为包含非捕获组 (?:)

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

现在,我的结果如下:

Match "https://stackoverflow.com/"
     Group 1: "stackoverflow.com"
     Group 2: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"

看到?第一组尚未被捕获。解析器使用它来匹配文本,但稍后在最终结果中忽略它。


编辑:

根据要求,让我也尝试解释群组。

好吧,团体服务于很多目的。它们可以帮助您从更大的匹配(也可以命名)中提取确切的信息,它们允许您重新匹配先前匹配的组,并且可以用于替换。我们试试一些例子,好吗?

好吧,想象一下你有某种XML或HTML(请注意这一点 正则表达式可能不是这项工作的最佳工具,但作为一个例子很好)。你想解析标签,所以你可以做这样的事情(我添加了空格,以便更容易理解):

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>

第一个正则表达式具有命名组(TAG),而第二个正则表达式使用公共组。两个正则表达式都做同样的事情:它们使用第一组中的值(标记的名称)来匹配结束标记。区别在于第一个使用名称来匹配值,第二个使用组索引(从1开始)。

我们现在尝试一些替换。请考虑以下文本:

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.

现在,让我们使用这个愚蠢的正则表达式:

\b(\S)(\S)(\S)(\S*)\b

此正则表达式匹配至少包含3个字符的单词,并使用组来分隔前三个字母。结果如下:

Match "Lorem"
     Group 1: "L"
     Group 2: "o"
     Group 3: "r"
     Group 4: "em"
Match "ipsum"
     Group 1: "i"
     Group 2: "p"
     Group 3: "s"
     Group 4: "um"
...

Match "consectetuer"
     Group 1: "c"
     Group 2: "o"
     Group 3: "n"
     Group 4: "sectetuer"
...

所以,如果我们应用替换字符串......

$1_$3$2_$4

...在它上面,我们尝试使用第一组,添加下划线,使用第三组,然后是第二组,添加另一个下划线,然后是第四组。结果字符串将如下所示。

L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.

您也可以使用命名组进行替换 ${name}

为了玩正则表达式,我推荐 http://regex101.com/,提供有关正则表达式如何工作的大量细节;它还提供了一些正则表达式引擎可供选择。


1839
2017-08-18 15:39



@ajsie:如果您对结果执行替换操作,则传统(捕获)组最有用。这是一个例子,我抓住逗号分隔的姓氏和名字,然后反转他们的顺序(感谢命名组)... regexhero.net/tester/?id=16892996-64d4-4f10-860a-24f28dad7e30 - Steve Wortham
不,这不一样。 - Ricardo Nolde
可能还指出,当使用正则表达式作为拆分分隔符时,非捕获组是非常有用的:“Alice和Bob”-split“\ s +(?: and | or)\ s +” - Yevgeniy
将非捕获组(?:)和前瞻和后瞻断言(?=,?!)之间的区别进行解释会很有趣。我刚开始学习正则表达式,但据我所知,非捕获组用于匹配和“返回”它们匹配的内容,但“返回值”不是“存储”用于反向引用。另一方面,Lookahead和lookbehind断言不仅没有“存储”,它们也不是匹配的一部分,它们只是断言某些东西会匹配,但是如果我没有弄错的话,它们的“匹配”值会被忽略。 (我大概是对的吗?) - Christian
[]是一套; [123]匹配集合中的任何字符一次; [^ 123]匹配一次不在集合内的任何东西; [^ / \ r \ n] +匹配一个或多个与/,\ r,\ n不同的字符。 - Ricardo Nolde


您可以使用捕获组来组织和解析表达式。非捕获组具有第一个好处,但没有第二个的开销。例如,您仍然可以说非捕获组是可选的。

假设你想匹配数字文本,但有些数字可以写成第1,第2,第3,第4 ......如果你想捕获数字部分而不是(可选)后缀你可以使用非捕获组。

([0-9]+)(?:st|nd|rd|th)?

这将匹配形式1,2,3 ......或者形式为1st,2nd,3rd,......但它只会捕获数字部分。


135
2017-08-18 13:24





?: 当您想要对表达式进行分组时使用,但您不希望将其保存为字符串的匹配/捕获部分。

一个例子是匹配IP地址:

/(?:\d{1,3}\.){3}\d{1,3}/

请注意,我不关心保存前3个八位字节,但是 (?:...) 分组允许我缩短正则表达式而不会产生捕获和存储匹配的开销。


87
2017-08-18 13:22





它使组不捕获,这意味着该组匹配的子字符串不会包含在捕获列表中。 ruby中的一个例子来说明差异:

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]

27
2017-08-18 13:23





历史动机:可以使用括号来解释非捕获组的存在。考虑表达式(a | b)c和a | bc,由于串联的优先级超过|,这些表达式分别代表两种不同的语言({ac,bc}和{a,bc})。但是,括号也用作匹配组(如其他答案所解释的那样)。

如果要使用括号但不捕获子表达式,则使用非捕获组。在示例中,(?:a | b)c


14
2018-02-04 08:07



我想知道为什么。我认为“为什么”对于记忆这些信息至关重要。 - J.M.I. MADISON


那些团体 捕获 你可以在以后的正则表达式中使用来匹配 要么 你可以在正则表达式的替换部分使用它们。制作一个 非捕获 group简单地免除了该组被用于上述任何一个原因。

如果您尝试捕获许多不同的东西并且有一些您不想捕获的组,则非捕获组很棒。

这就是它们存在的原因。在了解群体的同时,了解群体 原子团,他们做了很多!还有外观组,但它们有点复杂,并没有那么多使用。

稍后在正则表达式中使用的示例(反向引用):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>  [查找xml标记(不支持ns)]

([A-Z][A-Z0-9]*) 是一个捕获组(在这种情况下,它是标记名)

后来在正则表达式是 \1 这意味着它只匹配第一组中的相同文本( ([A-Z][A-Z0-9]*) group)(在这种情况下,它匹配结束标记)。


12
2017-08-18 13:22



你能给出一个简单的例子,说明它将如何用于匹配OR吗? - never_had_a_name
我的意思是你可以使用以后匹配,或者你可以在替换中使用它。或者在那句话中只是为了向您展示捕获组有两种用途 - Bob Fincheimer


让我试试这个例子: -

正则代码: - (?:animal)(?:=)(\w+)(,)\1\2

搜索字符串: -

第1行 - 动物=猫,狗,猫,老虎,狗

第2行 - 动物=猫,猫,狗,狗,老虎

第3行 - 动物=狗,狗,猫,猫,老虎

(?:animal)  - >非捕获组1

(?:=) - >非捕获组2

(\w+) - >捕获组1

(,) - >捕获组2

\1  - >捕获组1的结果,即第1行是猫,第2行是猫,第3行是狗。

\2  - >捕获组2的结果,即逗号(,)

因此,在此代码中,通过给出\ 1和\ 2,我们分别在代码中回忆或重复捕获的组1和2的结果。

根据代码的顺序(?:animal)应该是第1组,(?:=)应该是第2组并继续..

但是通过给出?:我们使匹配组不被捕获(在匹配的组中不计数,因此分组编号从第一个捕获的组开始而不是非捕获的组),以便重复匹配的结果-group(?:animal)以后不能在代码中调用。

希望这能解释非捕获组的使用。

在此处输入图像描述


8
2018-01-19 11:36





我是一名JavaScript开发人员,将尝试解释其与JavaScript有关的重要性。

考虑一下您想要匹配的场景 cat is animal 当你想要匹配猫和动物,两者都应该有一个 is 在他们之间。

 // this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]

 // using lookahead pattern it will match only "cat" we can
 // use lookahead but the problem is we can not give anything
 // at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]

 //so I gave another grouping parenthesis for animal
 // in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]

 // we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]

6
2018-03-01 09:43





在复杂的正则表达式中,您可能会遇到希望使用大量组的情况,其中一些组用于重复匹配,其中一些组用于提供反向引用。默认情况下,匹配每个组的文本将加载到后向引用数组中。在我们有很多组并且只需要能够从后向引用数组中引用其中一些的情况下,我们可以覆盖此默认行为以告诉正则表达式某些组仅用于重复处理而不需要捕获和存储在后向引用数组中。


5
2018-03-08 17:33