协慌网

登录 贡献 社区

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

如何?:被使用以及它有什么用?

答案

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

请考虑以下文本:

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

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

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

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

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

?:用于组合表达式时使用,但不希望将其保存为字符串的匹配 / 捕获部分。

一个例子是匹配 IP 地址:

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

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