正则表达式中的非捕获组是什么?

部署运行你感兴趣的模型镜像

非捕获组(即(?:) )如何在正则表达式中使用,它们有什么用?


#1楼

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


#2楼

?:用于对表达式进行分组,但又不想将其另存为字符串的匹配/捕获部分。

一个例子就是匹配IP地址的东西:

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

请注意,我不在乎保存前3个八位位组,但是(?:...)分组使我可以缩短正则表达式,而不会产生捕获和存储匹配项的开销。


#3楼

捕获的组可以稍后在正则表达式中使用以进行匹配, 或者可以在正则表达式的替换部分中使用它们。 建立一个非捕获组只是出于以下两个原因之一而使该组免于使用。

如果您要捕获许多不同的事物,并且有一些您不想捕获,则非捕获组非常有用。

那几乎就是它们存在的原因。 在学习组的同时,了解原子组 ,它们可以发挥很多作用! 也有环视组,但它们稍微复杂些,使用量很少。

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

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

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

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


#4楼

它使该组不被捕获,这意味着与该组匹配的子字符串将不包括在捕获列表中。 红宝石中的一个例子来说明不同之处:

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

#5楼

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

假设您要匹配数字文本,但是某些数字可以写为1st,2nd,3rd,4th...。如果要捕获数字部分,而不是(可选)后缀,则可以使用非捕获组。

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

这将匹配格式为1、2、3 ...或1st,2nd,3rd ...的数字,但是它将仅捕获数字部分。


#6楼

让我尝试用一​​个例子来解释一下。

考虑以下文本:

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

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

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

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

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"

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

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

现在,我的结果如下所示:

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"

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


编辑:

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

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

好的,假设您有某种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/ ,它提供了大量有关正则表达式工作原理的详细信息。 它还提供了一些正则表达式引擎供您选择。


#7楼

历史动机:非捕获性基团的存在可以用括号来解释。 考虑表达式(a | b)c和a | bc,由于连接的优先级高于|,因此这些表达式代表两种不同的语言(分别为{ac,bc}和{a,bc})。 但是,括号也用作匹配组(如其他答案所解释...)。

当您想使用括号而不捕获子表达式时,可以使用NON-CAPTURING GROUPS。 在示例中,(?:a | b)c


#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"]

#9楼

让我用一个例子来尝试:-

正则表达式代码:-(?: (?: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行是cat,第2行是cat,第3行是dog。

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

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

按照代码的顺序(?:animal)应该是组1,(?:=)应该是2组并继续。

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

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

在此处输入图片说明


#10楼

我想我会给你答案,不要在没有检查匹配成功的情况下使用捕获变量。

捕获变量$ 1等无效,除非匹配成功,并且也不会清除它们。

#!/usr/bin/perl  
use warnings;
use strict;   
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
    print "Fred wants a  $1";
}
else
{
    print "Fred dont wants a $1 $2";
}

在上面的示例中,为避免捕获$ 1中的bronto,使用(?:)。 如果模式匹配,则$ 1被捕获为下一个分组模式。 因此,输出将如下所示:

Fred wants a burger

如果您不想保存匹配项,这将很有用。


#11楼

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

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

输入网址字符串:

var url = "http://www.ora.com:80/goodparts?q#fragment";

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

console.debug(parse_url_regex.exec(url));

在此处输入图片说明

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

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


#12楼

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

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

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


#13楼

打开您的Google Chrome devTools,然后打开“控制台”标签:并输入以下内容:

"Peace".match(/(\w)(\w)(\w)/)

运行它,您将看到:

["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]

JavaScript RegExp引擎捕获三组,索引为1,2,3的项目。 现在使用非捕获标记来查看结果。

"Peace".match(/(?:\w)(\w)(\w)/)

结果是:

["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]

这显然是非捕获组。


#14楼

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

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

现在说,您只需要地址的id部分。 您想要做的就是获取匹配结果的第一组,在正则表达式中用()包围,而执行此操作的方法是使用非捕获组语法,即?: 。 因此,正则表达式(\\p{Alpha}*[az])(?:@example.com)将仅返回电子邮件的id部分。


#15楼

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

所以正则表达式是

([0-9]+)(?:January|February)?

就这么简单。

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值