当谈到 .NET 的正则表达式语言时,我对“组”和“捕获”之间的区别有点模糊。考虑以下 C# 代码:
MatchCollection matches = Regex.Matches("{Q}", @"^\{([A-Z])\}$");
我希望这会导致对字母“Q”进行一次捕获,但是如果我打印返回的属性MatchCollection,我会看到:
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”的捕获?
matches[0].Captures
你不会是第一个对此感到模糊的人。以下是著名的Jeffrey Friedl对此的评价(第 437 页以上):
根据您的观点,它要么为匹配结果增加了一个有趣的新维度,要么增加了混乱和臃肿。
更进一步:
Group 对象和 Capture 对象之间的主要区别在于,每个 Group 对象都包含一个 Capture 集合,表示该组在匹配过程中的所有 中间 匹配,以及该组匹配的最终文本。
几页后,这是他的结论:
在了解了 .NET 文档并真正了解了这些对象添加的内容之后,我对它们的感觉很复杂。一方面,这是一项有趣的创新 [..] 另一方面,它似乎增加了在大多数情况下不会使用的功能的效率负担 [..]
换句话说:它们非常相似,但偶尔,当它发生时,你会发现它们的用途。在你长出另一根白胡子之前,你甚至可能会喜欢 Captures ......
由于上述内容以及其他帖子中所说的内容似乎都没有真正回答您的问题,因此请考虑以下内容。将 Captures 视为一种历史跟踪器。当正则表达式进行匹配时,它从左到右遍历字符串(暂时忽略回溯),当遇到匹配的捕获括号时,它将存储在$x(x 是任何数字)中,比方说$1。
$x
$1
正常的正则表达式引擎,当要重复捕获括号时,将丢弃当前$1值并将其替换为新值。不是 .NET,它将保留此历史记录并将其放在Captures[0].
Captures[0]
如果我们将您的正则表达式更改为如下所示:
MatchCollection matches = Regex.Matches("{Q}{R}{S}", @"(\{[A-Z]\})+");
您会注意到第一组Group将有一个Captures(第一组始终是整个匹配,即等于$0),第二组将保持{S},即只有最后一个匹配组。但是,这里是捕获,如果您想找到其他两个捕获,它们在 中Captures,其中包含{Q} {R}和的所有中间捕获{S}。
Group
Captures
$0
{S}
{Q}
{R}
如果您想知道如何从多重捕获中获得,它只显示与字符串中明显存在的单个捕获的最后匹配,您必须使用Captures.
最后一个问题的最后一句话:总比赛总是有一个总捕获,不要将它与各个组混合。 捕获仅在组内有趣。