非捕获组,即(?:)
,如何在正则表达式中使用,它们有什么用?
让我试着用一个例子来解释这个问题。
考虑以下文字。
<!--语言。 none -->
http://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex
现在,如果我在它上面应用下面的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的主机和路径。
所以,我把regex改成包括非捕获组(?:)
。
(?: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(要知道[regex可能不是最好的工具][1],但它作为一个例子是不错的)。 你想解析这些标签,所以你可以这样做(我加了空格,以便于理解)。
<!--语言。 none -->
\<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
\<(.+?)\> [^<]*? \</\1\>
第一个regex有一个命名组(TAG),而第二个regex使用一个普通组。 两种regex都做了同样的事情:它们使用第一组的值(标签的名称)来匹配关闭标签。 它们使用第一个组的值(标签的名称)来匹配关闭标签。 不同的是,第一条使用名称来匹配值,而第二条使用组索引(从1开始)。
让我们'现在尝试一些替换。 考虑下面的文本。
<!--语言。 none -->
Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.
现在,让我们在它上面使用这个愚蠢的regex。
\b(\S)(\S)(\S)(\S*)\b
这个 regex 匹配至少有 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"
...
所以,如果我们应用替换字符串。
<!--语言: none -->
$1_$3$2_$4
... 在它上面,我们试图使用第一组,添加一个下划线,使用第三组,然后是第二组,再添加一个下划线,然后是第四组。 由此产生的字符串就会像下面这样。
<!--语言。 none -->
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}
。
要想玩转 regex,我推荐 [http://regex101.com/][2],它提供了大量关于 regex 工作原理的细节。 它还提供了一些 regex 引擎供您选择。
[1]: https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags [2]: http://regex101.com/
捕获的组,你可以在后面的词组中使用,以匹配或你可以在词组的替换部分使用它们。 制作一个不捕获的**组,只是为了避免该组被用于上述任何一种原因。
如果你想捕获许多不同的东西,而有些组你不想捕获,那么非捕获组就非常好。
这几乎就是它们存在的原因。 当你在学习组的时候,请学习原子组,它们可以做很多事情 也有环视组,但它们更复杂一些,用得不多。
在regex中后期使用的例子(backreference)。
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</1>
[ 找到一个xml标签(没有ns支持)] 。
([A-Z][A-Z0-9]*)
是一个捕获组(在此情况下是tagname)。
在这个词组的后面是1
,这意味着它只匹配第一组(([A-Z][A-Z0-9]*)
组)中的相同文本(在这种情况下,它是匹配结束标签)。
让我举个例子试试:-
Regex代码:-(?:动物)(?:=)(\w+)(,)\1\2
。
搜索字符串:-
第1行 - 动物=猫、狗、猫、虎、狗
。
第2行--animal=cat,cat,dog,dog,tiger
。
第3行--animal=dog,dog,cat,cat,tiger
。
(?:动物)
->
未捕获的第1组
(?:=)
-->
未捕获的第2组
(\w+)
-->
捕获的第1组
(,)
->
捕获的第2组
1
-->
捕捉到的第1组的结果,即第1行是猫,第2行是猫,第3行是狗。
2
-->捕捉到的第2组的结果,即逗号(,)
因此,在这个代码中,通过给出①和②,我们在代码的后面分别调用或重复捕获的第1组和第2组的结果。
按照代码的顺序(?:animal)应该是第1组,(?:=)应该是第2组并继续......。
但通过给出的? 我们使匹配组不被捕获(在匹配组中不计数,所以分组号从第一个捕获组开始,而不是非捕获组),这样匹配组(?:animal)的重复结果就不能在代码后面调用。
希望这能解释非捕获组的使用。
在此输入图片描述][1]
好吧,我是一个JavaScript开发人员,将尝试解释其与JavaScript相关的意义。
考虑一个场景,你想匹配 "猫是动物"。 当你想把猫和动物匹配起来,并且两者之间应该有一个 "是 "字。
// 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"]
tl;dr非捕获组,顾名思义,就是你不希望被包含在匹配中的regex部分,而:
是定义一个组为非捕获组的方法。
假设你有一个电子邮件地址[email protected]
。
下面的regex将创建两个组,id部分和@example.com部分。
(\p{Alpha}*[a-z])(@example.com)
。
为了简单起见,我们提取的是整个域名,包括@
字符。
现在让我们'说,你只需要地址的id部分。
你要做的是抓取匹配结果的第一组,在regex中用()
包围,这样做的方法是使用非捕获组语法,即
?:
.
所以regex(\p{Alpha}*[a-z])(?:@example.com)
将只返回邮件的id部分。
我遇到的一个有趣的事情是,你可以在一个非捕获组中拥有一个捕获组。 请看下面的regex来匹配网站URL。
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";
我的regex中的第一组(?:([A-Za-z]+):)
是一个非捕获组,它与协议方案和冒号:
字符相匹配,即:http:
。
http:
但是当我运行下面的代码时,我看到返回数组的第1个索引包含了字符串http
,而我想http
和冒号:
都不会被报告,因为它们在一个非捕获组中。
console.debug(parse_url_regex.exec(url));
[![在此输入图像描述][1]][1]
我想如果第一个组(?:([A-Za-z]+):)
是一个非捕获组,那么为什么它在输出数组中返回http
字符串。
所以如果你注意到在非捕获组里面有一个嵌套组([A-Za-z]+)
。
那个嵌套组([A-Za-z]+)
本身就是一个捕获组(开头没有?:
),在非捕获组(?:([A-Za-z]+):)
里面。
这就是为什么文本http
仍然会被捕获,但冒号:
字符在非捕获组内,但在捕获组外,不会在输出数组中被报告。
我想我会给你答案的。 不要在没有检查匹配是否成功的情况下使用捕获变量。
除非匹配成功,否则捕获变量、$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中的布朗图,使用了(?:)。 如果模式被匹配,那么$1将作为下一个分组模式被捕获。 因此,输出结果如下。
Fred wants a burger
如果你不想保存火柴,它是有用的。
打开你的谷歌浏览器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)/)
结果是:{{{7395924}}。
["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]
这很明显,什么是非捕获组。
它非常简单,我们可以理解与简单的日期的例子,假设如果日期被提到作为2019年1月1日或2019年5月2日或任何其他日期,我们只是想将其转换为dd/mm/yyyy格式,我们将不需要月'的名称,这是1月或2月的问题,所以为了捕获数字部分,但不是(可选)后缀,你可以使用一个非捕获组。
所以正则表达式将是
([0-9]+)(?:January|February)?
就这么简单