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

2024-03-28 14:15:05 发布

您现在位置:Python中文网/ 问答频道 /正文

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


3条回答

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

例如,与IP地址匹配的内容:

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

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

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

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

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

它将匹配表格1,2,3中的数字。。。或以表格1,2,3,。。。但它只能捕获数字部分。

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

考虑以下文本:

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的主机和路径。因此,我将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 may not be the best tool for the job,但作为一个例子很好)。您需要解析标记,以便可以执行以下操作(我添加了空格以便于理解):

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>

第一个regex有一个命名组(TAG),而第二个regex使用一个公共组。两个正则表达式都做同样的事情:它们使用第一个组的值(标记的名称)来匹配结束标记。区别在于第一个使用名称来匹配值,第二个使用组索引(从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}

为了使用regex,我建议使用http://regex101.com/,它提供了关于regex如何工作的大量细节;它还提供了一些可供选择的regex引擎。

相关问题 更多 >