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


当前回答

它使组不捕获,这意味着该组匹配的子字符串将不包含在捕获列表中。ruby中的一个示例说明了区别:

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

其他回答

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

让我举个例子:

Regex代码:(?:animal)(?:=)(\w+)(,)\1\2

搜索字符串:

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

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

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

(?:动物)-->未捕获组1

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

(\w+)-->捕获的组1

(,)-->捕获的组2

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

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

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

根据代码的顺序,(?:动物)应为第1组,(?=)应为2组,并继续。。

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

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

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

所以正则表达式将是,

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

就这么简单。

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

考虑以下文本:

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(请注意,regex可能不是这项工作的最佳工具,但它是一个很好的示例)。你想解析标签,所以你可以这样做(我添加了空格以便于理解):

   \<(?<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/,提供了有关正则表达式工作方式的大量详细信息;它还提供了一些正则表达式引擎供选择。

(?:…)充当一个组(…),但不捕获匹配的数据。它确实比标准的捕获组效率高得多。当你想对某个东西进行分组,但以后不需要重用它时,就可以使用它了@托托