.NETの正規表現言語に関しては、「グループ」と「キャプチャ」の違いが少し曖昧です。次のC#コードを検討してください。
MatchCollection matches = Regex.Matches("{Q}", @"^\{([A-Z])\}$");
これにより、文字「Q」のキャプチャが1つになると予想されますが、返された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オブジェクトには、一致中にグループによって一致するすべてのintermediaryを表すCaptureのコレクションが含まれることです。 、グループに一致する最終テキスト。
そして数ページ後、これが彼の結論です。
.NETのドキュメントを読み、これらのオブジェクトが追加するものを実際に理解した後、私はそれらについて複雑な気持ちになりました。一方で、それは興味深い革新です[..]一方で、大部分のケースで使用されない機能の効率の負担[..]を追加するようです
言い換えれば、これらは非常に似ていますが、ときどき発生する場合、それらの使用法を見つけることができます。別の灰色のひげを生やす前に、キャプチャを好むこともあります...
上記も、他の投稿で述べられていることもあなたの質問に答えているようには見えないので、次のことを考慮してください。 Capturesは一種の履歴トラッカーと考えてください。正規表現が一致すると、文字列を左から右へ(バックトラックをしばらく無視します)、一致するキャプチャ括弧に遭遇すると、それを$x
(xは任意の数字)に格納します。 $1
と言います。
通常の正規表現エンジンでは、キャプチャの括弧が繰り返される場合、現在の$1
が破棄され、新しい値に置き換えられます。 .NETではなく、この履歴を保持してCaptures[0]
に配置します。
次のように正規表現を変更した場合:
MatchCollection matches = Regex.Matches("{Q}{R}{S}", @"(\{[A-Z]\})+");
最初のGroup
には1つのCaptures
(最初のグループは常に完全一致、つまり$0
と等しい)があり、2番目のグループには{S}
、つまり最後に一致したグループのみ。ただし、これがキャッチです。他の2つのキャッチを見つけたい場合は、Captures
にあり、{Q}
{R}
および{S}
のすべての中間キャプチャが含まれています。 。
文字列内に明確に存在する個々のキャプチャへの最後の一致のみを表示する複数キャプチャからどのように取得できるか疑問に思った場合は、Captures
を使用する必要があります。
最後の質問の最後の言葉:合計マッチには常に合計キャプチャが1つあります。これを個々のグループと混在させないでください。キャプチャはグループ内でのみ興味深いものです。
グループは、正規表現でグループに関連付けたものです
"(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 "
最後の質問については、これを調べる前に、Capturesは所属するグループによって順序付けられたキャプチャの配列であると考えていました。むしろ、groups [0] .Capturesへの単なるエイリアスです。かなり役に立たない..
MSDNから ドキュメント :
Capturesプロパティの実際のユーティリティは、グループが1つの正規表現で複数のサブストリングをキャプチャするように、キャプチャグループに数量詞が適用されるときに発生します。この場合、Groupオブジェクトには最後にキャプチャされた部分文字列に関する情報が含まれ、Capturesプロパティにはグループによってキャプチャされたすべての部分文字列に関する情報が含まれます。次の例では、正規表現\ b(\ w +\s *)+。ピリオドで終わる文全体に一致します。グループ(\ w +\s *)+は、コレクション内の個々の単語をキャプチャします。 Groupコレクションには最後にキャプチャされた部分文字列に関する情報のみが含まれているため、文の最後のWord "sentence"をキャプチャします。ただし、グループによってキャプチャされた各Wordは、Capturesプロパティによって返されたコレクションから利用できます。
これは簡単な例(および写真)で説明できます。
_3:10pm
_を正規表現_((\d)+):((\d)+)(am|pm)
_と照合し、Monoインタラクティブcsharp
を使用:
_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" }
_
4番目のグループに一致する複数の数字があるため、グループを参照する場合(つまり、暗黙の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" }
_
この記事 の礼儀。
次のテキスト入力dogcatcatcat
とdog(cat(catcat))
のようなパターンがあるとします
この場合、3つのグループがあり、最初のグループ(major group)は一致に対応します。
一致== 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
)だけを分析しましょう。
ご覧のとおり、3つのマイナーグループ: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
)だけを分析しましょう。
マイナーグループgroup4
とgroup5
は、(...){3}
のためにありません繰り返し({n}ここでn> = 2)それらは1つの単一グループgroup3
にマージされました。
この場合、group3
値はcapture2
に対応します(最後のキャプチャ、言い換えれば)。
したがって、3つの内部キャプチャ(capture0
、capture1
、capture2
)をすべて必要とする場合、グループのCaptures
コレクションを循環する必要があります。
包含とは、パターンのグループを設計する方法に注意を払うことです。 (...)(...)
、(...){2}
、または(.{3}){2}
などのように、グループの仕様をどのような動作が引き起こすかを事前に考える必要があります。
うまくいけばCaptures、Groups、およびMatchesの違いを明らかにするのに役立つでしょう。