Ric*_*lde 2189

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

请考虑以下文本:

http://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex
Run Code Online (Sandbox Code Playgroud)

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

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
Run Code Online (Sandbox Code Playgroud)

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

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

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "https"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"
Run Code Online (Sandbox Code Playgroud)

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

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
Run Code Online (Sandbox Code Playgroud)

现在,我的结果如下:

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

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"
Run Code Online (Sandbox Code Playgroud)

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


编辑:

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

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

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

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>
Run Code Online (Sandbox Code Playgroud)

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

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

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.
Run Code Online (Sandbox Code Playgroud)

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

\b(\S)(\S)(\S)(\S*)\b
Run Code Online (Sandbox Code Playgroud)

此正则表达式匹配至少包含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"
...
Run Code Online (Sandbox Code Playgroud)

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

$1_$3$2_$4
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

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

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

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

Bil*_*ard 165

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

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

([0-9]+)(?:st|nd|rd|th)?
Run Code Online (Sandbox Code Playgroud)

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

  • 如果没有非捕获组,我可以这样做:`([0-9]+)(st|nd|rd|th)?`?使用“\1”我就有了数字,不需要“?:”。顺便说一句,最后的“?”是什么? (2认同)

RC.*_*RC. 101

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

一个例子是匹配IP地址:

/(?:\d{1,3}\.){3}\d{1,3}/
Run Code Online (Sandbox Code Playgroud)

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

  • 对于没有经验的读者:这会匹配 IP 地址,但也会匹配无效的 IP 地址。“验证”IP 地址的表达式要复杂得多。因此,不要用它来验证 IP 地址。 (7认同)

sep*_*p2k 36

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

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
Run Code Online (Sandbox Code Playgroud)


小智 22

一个简单的答案

使用它们来确保此处出现几种可能性之一(?:one|two)或可选短语camp(?:site)?或一般情况下您想要建立组/短语/部分的任何地方,而无需具体引用它。

他们将您捕获的群体数量降至最低。


小智 19

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

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

  • 我想知道为什么.我认为"为什么"对于记忆这些信息至关重要. (4认同)

Bob*_*mer 14

捕获您的组可以在以后的正则表达式中使用以匹配OR,您可以在正则表达式的替换部分中使用它们.使非捕获组简单地免除该组被用于上述任何一个原因.

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

这就是他们存在的原因.在您了解团体的过程中,了解Atomic Groups,他们会做很多事情!还有外观组,但它们有点复杂,并没有那么多使用.

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

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

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

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


小智 13

让我试试这个例子: -

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

搜索字符串: -

第1行 - animal=cat,dog,cat,tiger,dog

第2行 - animal=cat,cat,dog,dog,tiger

3号线 - animal=dog,dog,cat,cat,tiger

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

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

(\w+)- >捕获组1

(,)- >捕获第2组

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

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

所以在这段代码中,通过给出\ 1和\ 2,我们分别在代码中回忆或重复捕获的组1和2的结果.

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

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

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

在此输入图像描述


Gau*_*rav 8

我是一名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"]
Run Code Online (Sandbox Code Playgroud)


Jac*_*eng 7

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


Sco*_*son 7

我不能在最上面的答案上这样说:我想添加一个仅在最上面的答案中隐含的明确点:

非捕获组(?...) 不会从原始完全匹配中删除任何字符,它只会以可视化方式将正则表达式重新组织给程序员。

要访问正则表达式的特定部分而没有定义多余的字符,则始终需要使用 .group(<index>)

  • 您提供了其余答案中最重要的提示。我尝试了其中的所有示例,并使用了最合适的专有名词,因为没有得到理想的结果。只有您的帖子显示了我出了错。 (2认同)

Kan*_*mar 7

让我给你举一个地理坐标的例子,下面匹配两组

Latitude,Longitude

([+-]?\d+(?:\.\d+)?),([+-]?\d+(?:\.\d+)?)
Run Code Online (Sandbox Code Playgroud)

让我们拿一个([+-]?\d+(?:\.\d+)?)

坐标可以是整数,例如58或 可以是因此,提到了58.666
可选的 ( .666) 第二部分。(\.\d+)?

(...)? - for optional
Run Code Online (Sandbox Code Playgroud)

但括号里的是,那将是另一组比赛。我们不想要两个匹配,一个为58,另一个为.666,我们需要单个纬度作为匹配。非捕获组来了(?:)

非捕获组[+-]?\d+(?:\.\d+)?,58.666和58均为单场比赛


6pa*_*kid 6

tl; dr非捕获组,顾名思义是您不希望包含在匹配中的正则表达式部分,它?:是将组定义为非捕获组的一种方式。

假设您有一个电子邮件地址example@example.com。以下正则表达式将创建两个,即id部分和@ example.com部分。(\p{Alpha}*[a-z])(@example.com)。为简单起见,我们提取包括@字符在内的整个域名。

现在说,您只需要地址的id部分。您要做的是获取匹配结果的第一组,用()正则表达式将其括起来,而方法是使用非捕获组语法,即?:。因此,正则表达式(\p{Alpha}*[a-z])(?:@example.com)将仅返回电子邮件的id部分。

  • 我一直在努力理解这里所有的答案,直到我向下滚动到你的答案! (3认同)
  • 非捕获组也包含在比赛中。但他们没有被俘虏。这意味着当您列出捕获的组时,非捕获的组不在该列表中。它们也不能用于稍后在表达式中引用。即,如果您不关心域,则可以使用 `(\w+)@\w.\w.*` 并且您只会捕获 ID 部分。但是,如果它必须是 example.com,并且您想丢弃 example.com,因为所有好的匹配项都有它,那么您将使用非捕获组。`(\w+)@(?:example.com)`。 (2认同)

小智 6

(?: ... ) 作为一个组 ( ... ) 但不捕获匹配的数据。它确实比标准捕获组高效得多。当您想要对某些内容进行分组但稍后不需要重复使用时可以使用它。@托托


RBT*_*RBT 5

我遇到的一件有趣的事情是,您可以在一个非捕获组中包含一个捕获组。在下面的正则表达式中查找匹配的网址:

var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
Run Code Online (Sandbox Code Playgroud)

输入网址字符串:

var url = "http://www.ora.com:80/goodparts?q#fragment";
Run Code Online (Sandbox Code Playgroud)

正则表达式中的第一组(?:([A-Za-z]+):)是一个非捕获组,它与协议方案和冒号匹配,:http:当我在代码下运行http时,当我想到那http和冒号时,我看到返回数组的第一个索引包含字符串:两者都不会被举报,因为它们属于非捕获组。

console.debug(parse_url_regex.exec(url));
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我想如果第一组(?:([A-Za-z]+):)是一个非捕获组,那么为什么它http在输出数组中返回字符串。

因此,如果您注意到([A-Za-z]+)在非捕获组中有一个嵌套组。该嵌套组本身就是一个非捕获组内的([A-Za-z]+)捕获组(?:开始时没有)(?:([A-Za-z]+):)。这就是为什么http仍会捕获文本,但在:非捕获组内部但在捕获组外部的冒号字符不会在输出数组中报告的原因。


Nav*_*mad 5

它非常简单,我们可以通过简单的日期示例来理解,假设如果提到的日期为 2019 年 1 月 1 日或 2019 年 5 月 2 日或任何其他日期,我们只想将其转换为dd/mm/yyyy格式,我们不需要月份的名称是一月或二月,因此为了捕获数字部分,而不是(可选)后缀,您可以使用非捕获组。

所以正则表达式是

([0-9]+)(?:January|February)?
Run Code Online (Sandbox Code Playgroud)

就这么简单。