.NET正则expression式中的“组”和“捕获”有什么区别?
对于.NET的正则expression式语言,我对“组”和“捕获”之间的区别有些模糊。 考虑下面的C#代码:
MatchCollection matches = Regex.Matches("{Q}", @"^\{([AZ])\}$");
我期望这会导致一个字母'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页):
根据你的观点,这要么增加一个有趣的新维度的比赛结果,或增加混乱和膨胀。
还有:
Group对象和Capture对象之间的主要区别在于,每个Group对象都包含一个Captures集合,该集合表示该组在匹配过程中的所有中间匹配,以及该组匹配的最终文本。
几页之后,这是他的结论:
在通过.NET文档并真正理解这些对象的添加之后,我对他们有着复杂的感受。 一方面,这是一个有趣的创新[…]另一方面,它似乎增加了function的效率负担[…]在大多数情况下不会被使用
换句话说:它们非常相似,但是偶尔发生,你会发现它们的用处。 在你长出另一个灰胡子之前,你甚至可能会喜欢捕捉…
既然以上都没有,另一篇文章中的话也没有真正回答你的问题,可以考虑一下。 捕捉作为一种历史追踪器。 当正则expression式匹配时,它从左到右通过串(忽略回溯一会),当它遇到一个匹配的捕获括号时,它将存储在$x
(x是任何数字),比如说$1
。
正常的正则expression式引擎,当捕获圆括号将被重复时,将扔掉当前的$1
,并将用新值replace它。 不是.NET,它将保留这个历史并将其放置在Captures[0]
。
如果我们改变你的正则expression式如下:
MatchCollection matches = Regex.Matches("{Q}{R}{S}", @"(\{[AZ]\})+");
你会注意到第一个Group
将有一个Captures
(第一个组总是整个匹配,即等于$0
),第二个组将保存{S}
,即只有最后一个匹配组。 然而,如果你想要find另外两个捕获,那么这里就是Captures
,它们在Captures
,其中包含{Q}
{R}
和{S}
所有中间捕获。
如果你想知道如何从多次捕捉中获得,那么只能显示最后一次匹配,而不是显示在string中的单个捕捉,你必须使用Captures
。
最后一个问题的最后一个字:总的匹配总是有一个总捕获,不要混合个人组。 捕捉只在组内才有意思 。
从MSDN 文档 :
捕获属性的实际效用是将量词应用于捕获组,以便该组捕获单个正则expression式中的多个子string。 在这种情况下,Group对象包含有关上次捕获的子string的信息,而Captures属性包含有关该组捕获的所有子string的信息。 在以下示例中,正则expression式\ b(\ w + \ s *)+。 匹配一段以句号结束的句子。 组(\ w + \ s *)+捕获集合中的单个单词。 由于Group集合仅包含有关最后捕获的子string的信息,因此它会捕获句子中的最后一个单词“sentence”。 但是,由捕获属性返回的集合中可以find该组捕获的每个单词。
一个小组就是我们与正则expression式中的小组相关联的东西
"(a[zx](b?))" Applied to "axb" returns an array of 3 groups: group 0: axb, the entire match. group 1: axb, the first group matched. group 2: b, the second group matched.
除了这些只是“被俘”的群体。 非捕获组(使用'(?:'语法不在这里表示。
"(a[zx](?:b?))" Applied to "axb" returns an array of 2 groups: group 0: axb, the entire match. group 1: axb, the first group matched.
捕获也是我们与“捕获的群体”相关联的。 但是当多次应用一个量词时,只有最后一个匹配被保留为该组的匹配。 捕获数组存储所有这些匹配。
"(a[zx]\s+)+" Applied to "ax az ax" returns an array of 2 captures of the second group. group 1, capture 0 "ax " group 1, capture 1 "az "
至于你的最后一个问题 – 在考虑这个之前,我会想到,捕获将是他们所属组织所捕获的数组。 相反,它只是组的一个别名[0] .Captures。 很无用..
这可以用一个简单的例子来解释(和图片)。
((\d)+):((\d)+)(am|pm)
,使用Mono交互式csharp
匹配3:10pm
:
csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)"). > Groups.Cast<Group>(). > Zip(Enumerable.Range(0, int.MaxValue), (g, n) => "[" + n + "] " + g); { "[0] 3:10pm", "[1] 3", "[2] 3", "[3] 10", "[4] 0", "[5] pm" }
那么1在哪里?
由于在第四组上有多个匹配的数字,所以如果我们引用这个组,我们只是“最后一次匹配”(用一个隐式的ToString()
)。 为了暴露中间匹配,我们需要更深入地参考所Captures
属性:
csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)"). > Groups.Cast<Group>(). > Skip(4).First().Captures.Cast<Capture>(). > Zip(Enumerable.Range(0, int.MaxValue), (c, n) => "["+n+"] " + c); { "[0] 1", "[1] 0" }
由这篇文章提供 。
想象一下,你有以下的文本inputdogcatcatcat
和一个模式像dog(cat(catcat))
在这种情况下,您有3个组,第一个( 主要组 )对应于匹配。
匹配== dogcatcatcat
和Group0 == dogcatcatcat
Group1 == catcatcat
Group2 == catcat
那么这是关于什么的?
让我们考虑使用Regex
类在C#(.NET)中编写的一个小例子。
int matchIndex = 0; int groupIndex = 0; int captureIndex = 0; foreach (Match match in Regex.Matches( "dogcatabcdefghidogcatkjlmnopqr", // input @"(dog(cat(...)(...)(...)))") // pattern ) { Console.Out.WriteLine($"match{matchIndex++} = {match}"); foreach (Group @group in match.Groups) { Console.Out.WriteLine($"\tgroup{groupIndex++} = {@group}"); foreach (Capture capture in @group.Captures) { Console.Out.WriteLine($"\t\tcapture{captureIndex++} = {capture}"); } captureIndex = 0; } groupIndex = 0; Console.Out.WriteLine(); }
输出 :
match0 = dogcatabcdefghi group0 = dogcatabcdefghi capture0 = dogcatabcdefghi group1 = dogcatabcdefghi capture0 = dogcatabcdefghi group2 = catabcdefghi capture0 = catabcdefghi group3 = abc capture0 = abc group4 = def capture0 = def group5 = ghi capture0 = ghi match1 = dogcatkjlmnopqr group0 = dogcatkjlmnopqr capture0 = dogcatkjlmnopqr group1 = dogcatkjlmnopqr capture0 = dogcatkjlmnopqr group2 = catkjlmnopqr capture0 = catkjlmnopqr group3 = kjl capture0 = kjl group4 = mno capture0 = mno group5 = pqr capture0 = pqr
我们来分析一下第一场比赛( match0
)。
正如你所看到的,有三个小组 : group3
, group4
和group5
group3 = kjl capture0 = kjl group4 = mno capture0 = mno group5 = pqr capture0 = pqr
这些组(3-5)是由于主模式的子模式 (...)(...)(...)
(dog(cat(...)(...)(...)))
group3
值对应于捕获( capture0
)。 (如在组group4
和组group5
的情况下)。 这是因为没有像(...){3}
那样的组重复 。
好吧,让我们考虑另一个例子,其中有一个组重复 。
如果我们修改从(dog(cat(...)(...)(...)))
到(dog(cat(...){3}))
,你会注意到有下面的组重复 : (...){3}
。
现在输出已经改变了:
match0 = dogcatabcdefghi group0 = dogcatabcdefghi capture0 = dogcatabcdefghi group1 = dogcatabcdefghi capture0 = dogcatabcdefghi group2 = catabcdefghi capture0 = catabcdefghi group3 = ghi capture0 = abc capture1 = def capture2 = ghi match1 = dogcatkjlmnopqr group0 = dogcatkjlmnopqr capture0 = dogcatkjlmnopqr group1 = dogcatkjlmnopqr capture0 = dogcatkjlmnopqr group2 = catkjlmnopqr capture0 = catkjlmnopqr group3 = pqr capture0 = kjl capture1 = mno capture2 = pqr
再次,我们来分析一下第一场比赛( match0
)。
由于(...){3}
重复 ( {n},其中n> = 2 ),没有更多的次要群组 group4
和group5
,它们被合并成一个单独的group group3
。
在这种情况下, group3
值对应于它的capture2
(换句话说,最后一次捕获 )。
因此,如果您需要所有3个内部捕获( capture0
, capture0
, capture2
),则必须循环访问该组的Captures
集合。
包括:注意你devise你的模式组的方式。 (...)(...)
, (...){2}
或(.{3}){2}
等。
希望这将有助于揭示捕捉 , 团体和匹配之间的差异。