对于.NET的正则表达式语言,“组”和“捕获”之间的区别让我有些模糊。考虑以下C#代码:

MatchCollection matches = Regex.Matches("{Q}", @"^\{([A-Z])\}$");

我希望这会导致字母'Q'的单个捕获,但是如果我打印返回的MatchCollection的属性,则会看到:
matches.Count: 1
matches[0].Value: {Q}
        matches[0].Captures.Count: 1
                matches[0].Captures[0].Value: {Q}
        matches[0].Groups.Count: 2
                matches[0].Groups[0].Value: {Q}
                matches[0].Groups[0].Captures.Count: 1
                        matches[0].Groups[0].Captures[0].Value: {Q}
                matches[0].Groups[1].Value: Q
                matches[0].Groups[1].Captures.Count: 1
                        matches[0].Groups[1].Captures[0].Value: Q

这到底是怎么回事?我知道整个比赛也都有机会,但是小组如何进入?为什么matches[0].Captures不包含字母'Q'的捕获?

最佳答案

您不会是第一个对此感到困惑的人。这就是著名的Jeffrey Friedl关于它的说法(第437+页):



进一步:



几页后,这就是他的结论:



换句话说:它们非常相似,但是偶尔会碰巧有它们的用处。在长出另一头灰色 mustache 之前,您甚至可能喜欢Captures ...

由于以上内容或其他帖子中的内容都无法真正回答您的问题,请考虑以下内容。将Captures视为一种历史跟踪器。当正则表达式匹配时,它从左到右遍历字符串(暂时忽略回溯),当遇到匹配的捕获括号时,它将把它存储在$x(x是任何数字)中,比如$1

普通的正则表达式引擎,当要重复捕获括号时,将丢弃当前的$1并将其替换为新值。不是.NET,它将保留此历史记录并将其放置在Captures[0]中。

如果我们将您的正则表达式更改为如下所示:

MatchCollection matches = Regex.Matches("{Q}{R}{S}", @"(\{[A-Z]\})+");

您会注意到,第一个Group将具有一个Captures(第一个组始终是整个匹配项,即等于$0),第二个组将保留{S},即仅最后一个匹配组。但是,这是渔获物,如果要查找其他两个渔获物,它们都在Captures中,其中包含{Q} {R}{S}的所有中间捕获。

如果您想知道如何从多重捕获(仅显示最后一个匹配项与字符串中明显存在的单个捕获项)中得到的结果,则必须使用Captures

关于您的最后一个问题的最后一句话:总匹配始终具有总捕获,请勿将其与各个组混合使用。捕获仅在组内部有趣。

10-05 22:38