.NET normal ifadelerinde “gruplar” ve “yakalar” arasındaki fark nedir?


162

.NET'in normal ifade dili söz konusu olduğunda bir "grup" ve "yakalama" arasındaki farkın ne olduğunu biraz bulanık buluyorum. Aşağıdaki C # kodunu göz önünde bulundurun:

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

Bunun 'Q' harfi için tek bir yakalama ile sonuçlanmasını bekliyorum, ancak iade edilenlerin özelliklerini yazdırırsam MatchCollection, şunu görüyorum:

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

Burada tam olarak ne oluyor? Tüm maç için de bir yakalama olduğunu anlıyorum, ancak gruplar nasıl giriyor? Ve neden matches[0].Captures'Q' harfi için yakalamayı içermiyor?

Yanıtlar:


126

Bu konuda ilk bulanık olan siz olmayacaksınız. Ünlü Jeffrey Friedl'in bu konuda söyledikleri (sayfa 437+):

Görünümünüze bağlı olarak, maç sonuçlarına ilginç bir yeni boyut ekler veya karışıklık ve şişkinlik ekler.

Ve dahası:

Bir Group nesnesi ve bir Capture nesnesi arasındaki temel fark, her bir Group nesnesinin , maç sırasında grup tarafından yapılan tüm ara eşleşmeleri ve grup tarafından eşleşen son metni temsil eden bir Captures koleksiyonu içermesidir .

Ve birkaç sayfa sonra, bu onun sonucu:

.NET belgelerini geçmiş ve aslında bu nesnelerin neler eklediğini anladıktan sonra, onlar hakkında karışık duygular var. Bir yandan, bu ilginç bir yenilik [..], öte yandan çoğu durumda kullanılmayacak bir işlevsellik verimliliği yükü [..] ekliyor gibi görünüyor

Başka bir deyişle: çok benzerler, ancak bazen ve olduğu gibi, onlar için bir kullanım bulacaksınız. Başka bir gri sakal yetiştirmeden önce, Yakalamalara düşkün bile olabilirsiniz ...


Ne yukarıdakiler ne de diğer gönderide söylenenler sorunuzu gerçekten cevaplamıyor gibi göründüğü için aşağıdakileri göz önünde bulundurun. Yakalamaları bir tür tarih izleyici olarak düşünün. Normal ifade eşleştiğinde, dizeden soldan sağa geçer (bir an için geriye doğru izlemeyi yok sayarak) ve eşleşen bir yakalama paranteziyle karşılaştığında, bunu $x(x herhangi bir basamak olarak) içinde saklayalım diyelim $1.

Normal regex motorlar, yakalama parantezleri tekrarlanacaksa, akımı atar $1ve yeni değerle değiştirir. Bu geçmişi tutacak ve içine yerleştirecek .NET değil Captures[0].

Normal ifadenizi aşağıdaki gibi görünecek şekilde değiştirirsek:

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

ilkinin bir tane Groupolacağını Captures(ilk grubun her zaman tüm maç olduğunu, yani eşit olduğunu $0) ve ikinci grubun {S}, yani sadece son eşleşen grubu tutacağını fark edeceksiniz . Ancak, işte yakalama, eğer diğer iki yakalamayı bulmak istiyorsanız Captures, {Q} {R}ve için tüm aracı yakalamaları içeren bunlar {S}.

Eğer sadece dizede açıkça bulunan münferit yakalamalarla son eşleşmeyi gösteren çoklu yakalamadan nasıl alabileceğinizi merak ettiyseniz, kullanmalısınız Captures.

Son sorunuzda son bir kelime: toplam maçın her zaman bir toplam Yakalama vardır, bunu tek tek Gruplarla karıştırmayın. Yakalamalar sadece gruplar içinde ilginçtir .


1
a functionality that won't be used in the majority of casesSanırım tekneyi kaçırdı. Kısa vadede (?:.*?(collection info)){4,20}verimliliği yüzde yüz daha fazla arttırır.

1
@sln, neye atıfta bulunduğunuzdan ve 'o' olduğundan emin değilsiniz (friedl?). Verdiğiniz örnek, bu tartışmayla veya kullanılan ifadelerle alakasız görünüyor. Ayrıca, açgözlü olmayan niceleyiciler, açgözlü niceleyicilerden çok nadiren daha etkilidir ve girdi seti ve dikkatli perf testi hakkında bilgi gerektirir.
Abel

@Abel - Buraya yinelenen bir sorudan indim. Friedl'in alıntı yaptığını görüyorum. Bu yazı eskidir ve modern olması için yenilenmesi gerekir. Sadece Dot Net ile bu yapılabilir, diğerlerinden ayıran şey budur. Dağılın: Miktarı yakalamayan toplam grup örneği (?:..)+. .*?Yakalama alt ifadesine (grup) kadar her şeyi tembel olarak eşleştirin . Devam edin. Tek bir maçta, bir grup koleksiyonu, tam olarak ihtiyaç duyulan bir dizi diziyi hızlandırır. Daha sonra bulmaya gerek yok, 10 ila 20 veya daha fazla kez daha hızlı hale getiren yeniden giriş yok.

1
@sln, bu soru başka bir şeyle ilgilidir ve özellikle diğer regex motorlarında bulunmayan bir .net özelliğiyle ilgilidir (yakalamalara karşı gruplar, başlığa bakın). Burada modası geçmiş bir şey görmüyorum, .net hala aynı çalışıyor, aslında bu bölüm .net'te uzun bir süre değişmedi. Performans sorunun bir parçası değildir. Evet, yakalamayan gruplama daha hızlıdır, ancak yine, buradaki konu tam tersidir. Neden açgözlü tembel daha hızlı online ve friedl'ın kitap, ancak burada OT birçok metin açıklanmaktadır. Belki diğer soru (hangisi?) Gerçek bir kopya değildi?
Abel

2
@Abel - Bunu söylemeye devam ettiğimi biliyorum, ama duymuyorsun. Friedl'in bu açıklamasına uyuyorum a functionality that won't be used in the majority of cases. Aslında regex topraklarında en çok aranan işlevselliktir. Tembel / açgözlü? Bunun yorumlarım ile ne ilgisi var? Değişken miktarda yakalama arabelleği olmasını sağlar. Tüm dizeyi tek bir eşleştirmeyle tarayabilir. Eğer .*?(dog)bulgular ilk dogo zaman (?:.*?(dog))+bulacaksınız tüm dog tek maçında tüm dizesinde. Performans artışı dikkat çekicidir.

20

Bir grup, düzenli ifadelerle gruplarla ilişkilendirdiğimiz şeydir

"(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.

ancak bunlar sadece 'ele geçirilmiş' gruplar. Yakalamayan gruplar ('(?:' Sözdizimi kullanılarak) burada gösterilmez.

"(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.

Yakalama ayrıca 'yakalanan gruplar' ile ilişkilendirdiğimiz şeydir. Ancak, gruba bir nicelik belirteci birden çok kez uygulandığında, yalnızca son eşleşme grubun eşleşmesi olarak tutulur. Yakalama dizisi tüm bu eşleşmeleri depolar.

"(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 "

Son sorunuza gelince, bunu ele almadan önce, Captures'ın ait oldukları grup tarafından sipariş edilen bir dizi yakalama olacağını düşünürdüm. Daha ziyade bu sadece [0] gruplarının takma adıdır. Oldukça işe yaramaz ..


Açık açıklama (y)
Ghasan

19

Bu basit bir örnekle (ve resimlerle) açıklanabilir.

3:10pmNormal ifadeyle eşleştirme ((\d)+):((\d)+)(am|pm)ve Mono etkileşimli kullanma 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" }

Peki 1 nerede? resim açıklamasını buraya girin

Dördüncü grupta eşleşen birden fazla hane olduğundan, gruba referans verirsek (örtülü ToString(), yani) yalnızca son eşleşmeye "ulaşırız" . Ara maçları göstermek için daha derine inmeli ve Capturessöz konusu gruptaki mülke referans vermeliyiz :

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" }

resim açıklamasını buraya girin

Bu makalenin izniyle .


3
Güzel Makale. Bir resim bin kelime değerinde bir olup.
AlexWei

Sen bir yıldızsın.
mikemay

14

MSDN belgelerinden :

Captures özelliğinin gerçek faydası, bir yakalama grubuna bir nicelleştirici uygulandığında, grubun tek bir normal ifadede birden çok alt dizeyi yakalaması durumunda oluşur. Bu durumda, Group nesnesi en son yakalanan alt dize hakkında bilgi içerirken, Captures özelliği grup tarafından yakalanan tüm alt dizeler hakkında bilgi içerir. Aşağıdaki örnekte, \ b (\ w + \ s *) + normal ifadesi. bir süre içinde biten tüm bir cümle ile eşleşir. (\ W + \ s *) + grubu koleksiyondaki tek tek sözcükleri yakalar. Grup koleksiyonu yalnızca son yakalanan alt dize hakkında bilgi içerdiğinden, cümledeki son sözcüğü (cümle) yakalar. Ancak, grup tarafından yakalanan her sözcük, Captures özelliği tarafından döndürülen koleksiyondan kullanılabilir.


4

Aşağıdaki metin girişine dogcatcatcatve benzeri bir desene sahip olduğunuzu düşünündog(cat(catcat))

Bu durumda, 3 grubunuz var, birincisi ( büyük grup ) maça karşılık gelir.

Maç == dogcatcatcatve Grup0 ==dogcatcatcat

Grup1 == catcatcat

Grup2 == catcat

Peki bütün bunlar ne demek?

RegexSınıf kullanarak C # (.NET) ile yazılmış küçük bir örneği ele alalım .

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();
        }

Çıktı :

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

Sadece ilk maçı analiz edelim ( match0).

Gördüğünüz gibi orada üç şunlardır minör grupları : group3, group4vegroup5

    group3 = kjl
        capture0 = kjl
    group4 = mno
        capture0 = mno
    group5 = pqr
        capture0 = pqr

Bu gruplar, (3-5) için 'arasında oluşturulan alt modeli ' (...)(...)(...)bölgesinin temel desen (dog(cat(...)(...)(...)))

Değeri, group3yakalama ( capture0) öğesine karşılık gelir . (Durumunda olduğu gibi group4ve group5). Çünkü hiçbir grup tekrarlama vardır gibi (...){3}.


Tamam, bir grup tekrarının olduğu başka bir örneği ele alalım .

Biz dan (kod yukarıda gösterilen için) eşleştirilecek düzenli ifade deseni değiştirirseniz (dog(cat(...)(...)(...)))için (dog(cat(...){3})), aşağıdaki olduğunu fark edeceksiniz grup tekrarı : (...){3}.

Şimdi Çıktı değişti:

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

Yine, sadece ilk maçı analiz edelim ( match0).

Artık küçük gruplar yok group4 ve tekrarlamagroup5 nedeniyle ( {n} burada n> = 2 ) tek bir grupta birleştirildi .(...){3} group3

Bu durumda, group3değer ona capture2( son yakalama , diğer bir deyişle) karşılık gelir.

Tüm 3 iç yakalar ihtiyaç Böylece if ( capture0, capture1, capture2) sen grubun arasında geçiş yapmak gerekecek Captureskoleksiyonu.

Çözüm: modelinizin gruplarını nasıl tasarladığınıza dikkat edin. Sen davranış gibi grubun şartname neyin sebep peşin düşünmelidir (...)(...), (...){2}ya (.{3}){2}vs.


Umarım Yakalamalar , Gruplar ve Maçlar arasındaki farklara da ışık tutmaya yardımcı olur .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.