2010-07-23 14 views
128

Sono un po 'confuso su quale sia la differenza tra un "gruppo" e una "cattura" quando si tratta del linguaggio delle espressioni regolari di .NET. Si consideri il seguente codice C#:Qual è la differenza tra "gruppi" e "acquisizioni" nelle espressioni regolari .NET?

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

Mi aspetto che questo risultato in una singola acquisizione per la lettera 'Q', ma se stampo le proprietà della tornata MatchCollection, vedo:

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 

Cosa sta succedendo esattamente qui? Capisco che c'è anche una cattura per l'intera partita, ma come arrivano i gruppi? E perché lo matches[0].Captures non include la cattura per la lettera "Q"?

risposta

94

Non sarai il primo a essere sfocato. Ecco cosa il famoso Jeffrey Friedl ha da dire in proposito (pagine 437+):

A seconda del punto di vista, sia aggiunge una nuova dimensione interessante per gli coi risultati, o aggiunge confusione e gonfiare.

E più avanti:

La differenza principale tra un oggetto Gruppo e un oggetto di acquisizione è che ogni oggetto gruppo contiene una raccolta di catture che rappresentano tutti i intermedie partite da parte del gruppo durante la partita, così come il testo finale abbinato dal gruppo.

E poche pagine dopo, questa è la sua conclusione:

Dopo aver passato la documentazione di .NET ed effettivamente comprendere ciò che questi oggetti aggiungono, Ho sentimenti contrastanti su di loro. Su da un lato, è un'interessante innovazione [..] d'altro canto, lo sembra aggiungere un carico di efficienza [..] di una funzionalità che non saranno utilizzati nella maggior parte dei casi

In altre parole: sono molto simili, ma di tanto in tanto e, come accade, troverete un uso per loro. Prima di crescere un'altra barba grigia, si può anche ottenere appassionato delle catture ...


Dal momento che né quanto sopra, né ciò che è detto in altri post sembra davvero di rispondere alla tua domanda, considerare quanto segue. Pensa a Cattura come una sorta di tracker della storia. Quando la regex fa la sua corrispondenza, passa attraverso la stringa da sinistra a destra (ignorando il backtracking per un momento) e quando incontra una parentesi di cattura corrispondente, la memorizzerà in $x (x è una qualsiasi cifra), diciamo $1.

I normali motori regex, quando le parentesi di acquisizione devono essere ripetute, elimineranno l'attuale $1 e lo sostituiranno con il nuovo valore. Not .NET, che manterrà questa cronologia e la inserirà in Captures[0].

Se cambiamo il tuo regex a guardare come segue:

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

si noterà che il primo Group avrà una Captures (il primo gruppo essendo sempre tutta la partita, vale a dire, pari a $0) e la il secondo gruppo manterrà {S}, ovvero solo l'ultimo gruppo corrispondente. Tuttavia, e qui è il problema, se si desidera trovare le altre due prese, sono in Captures, che contiene tutte le acquisizioni intermedie per {Q}{R} e {S}.

Se vi siete mai chiesti come ottenere dall'acquisizione multipla, che mostra solo l'ultima corrispondenza alle singole acquisizioni chiaramente presenti nella stringa, è necessario utilizzare Captures.

Una parola finale sulla tua domanda finale: la partita totale ha sempre una cattura totale, non mescolare quella con i singoli gruppi. Le acquisizioni sono interessanti solo all'interno dei gruppi.

+1

Grazie, l'esempio concreto chiarisce! –

+0

'una funzionalità che non verrà utilizzata nella maggior parte dei casi' Penso che abbia perso la barca. A breve termine '(?:. *? (Informazioni sulla raccolta)) {4,20}' aumenta l'efficienza di più del poche centinaia di percento. – sln

+0

@sln, non so a cosa ti riferisci e chi è "lui" (fritto?). L'esempio che date sembra non correlato a questa discussione o alle espressioni usate. Inoltre, i quantificatori non avidi sono solo molto raramente più efficienti dei quantificatori grezzi e richiedono conoscenza del set di input e attenti test di perf. – Abel

14

da MSDN documentation:

La reale utilità della proprietà Cattura si verifica quando un quantificatore è applicato ad un gruppo di cattura in modo che il gruppo di cattura più sottostringhe in una singola espressione regolare. In questo caso, l'oggetto Gruppo contiene informazioni sull'ultima sottostringa acquisita, mentre la proprietà Acquisisce contiene informazioni su tutte le sottostringhe catturate dal gruppo. Nell'esempio seguente, l'espressione regolare \ b (\ w + \ s *) +. corrisponde a un'intera frase che termina in un periodo. Il gruppo (\ w + \ s *) + acquisisce le singole parole nella raccolta. Poiché la raccolta di gruppo contiene informazioni solo sull'ultima sottostringa acquisita, acquisisce l'ultima parola nella frase "frase". Tuttavia, ogni parola catturata dal gruppo è disponibile dalla collezione restituita dalla proprietà Capture.

14

un gruppo è quello che abbiamo associato a gruppi nelle espressioni regolari

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

eccezione del fatto che questi gruppi sono solo 'catturati'. gruppi non cattura (usando il '? (:' sintassi non sono rappresentati qui

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

Un Capture è anche ciò che abbiamo associato a 'gruppi catturati' Ma quando il gruppo viene applicato con un quantificatore più volte,.. solo l'ultima partita è mantenuto partita del gruppo I negozi cattura matrice tutte queste partite

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

quanto riguarda la tua ultima domanda -.. avrei pensato prima di guardare a questo che cattura sarebbe un allineamento della cattura ordinato dal gruppo a cui appartengono. Piuttosto è solo un alias per i gruppi [0] .Captures. Abbastanza inutile ..

2

Immaginate di avere la seguente immissione del testo dogcatcatcat e un modello come dog(cat(catcat))

In questo caso, si dispone di 3 gruppi, il primo (grande gruppo) corrisponde alla partita.

Partita == dogcatcatcat e gruppo0 == dogcatcatcat

Gruppo1 == catcatcat

Group2 == catcat

Quindi di cosa si tratta?

Consideriamo un piccolo esempio scritto in C# (.NET) usando la classe Regex.

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

uscita:

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 

Analizziamo solo la prima partita (match0).

Come potete vedere ci sono tre gruppi minori : group3, group4 e group5

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

Quei gruppi (3-5) sono stati creati a causa del 'subpattern' (...)(...)(...) del principale modello(dog(cat(...)(...)(...)))

Valore di group3 corrisponde alla sua cattura (capture0). (Come nel caso di group4 e group5). Questo perché non ci sono ripetizioni di gruppo come (...){3}.


Ok, prendiamo in considerazione un altro esempio in cui v'è una ripetizione gruppo.

Se modifichiamo il modello di espressione regolare da abbinare (per il codice mostrato sopra) (dog(cat(...)(...)(...)))-(dog(cat(...){3})), si noterà che non v'è la seguente ripetizione gruppo: (...){3}.

Ora il uscita è cambiato:

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 

Anche in questo caso, analizziamo solo la prima partita (match0).

Non ci sono altri gruppi minori group4 e group5 causa di (...){3}ripetizione ({n} cui n> = 2) che sono stati fusi in un unico gruppo group3.

In questo caso, il valore group3 corrisponde alla sua capture2 (dell'ultima cattura, in altre parole).

Quindi se avete bisogno di tutte le 3 catture interne (capture0, capture1, capture2) si dovrà passare da collezione del gruppo Captures.

ónonclusione: prestare attenzione al modo in cui si progettano i gruppi del modello. Si dovrebbe pensare in anticipo che cosa comportamento causa specifica del gruppo, come (...)(...), (...){2} o (.{3}){2} ecc


Speriamo che vi aiuterà a far luce sulle differenze tra Cattura, Gruppi e Partite come bene.

4

Questo può essere spiegato con un semplice esempio (e immagini).

corrispondenza 3:10pm con l'espressione regolare ((\d)+):((\d)+)(am|pm), e utilizzando Mono interattivo 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" } 

Allora dov'è il 1? enter image description here

Poiché ci sono più le cifre che corrispondono al quarto gruppo, abbiamo solo "arrivare a" l'ultima partita se vogliamo fare riferimento al gruppo (con un implicito ToString(), che è). Al fine di esporre le partite intermedie, abbiamo bisogno di andare più in profondità e fare riferimento alla proprietà Captures sul gruppo in questione:

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

enter image description here

Per gentile concessione di this article.

Problemi correlati