2010-04-08 8 views
12

Diciamo che ho due stringhe come questo:Regex: Corrispondenza contro i gruppi in ordine diverso, senza ripetere il gruppo

XABY 
XBAY 

Un semplice regex che corrisponde entrambi avrebbero andare in questo modo:

X(AB|BA)Y 

Tuttavia , Ho un caso in cui A e B sono stringhe complicate, e sto cercando un modo per evitare di dover specificare ciascuno di loro due volte (su ciascun lato del |). C'è un modo per farlo (presumibilmente è più semplice che doverli specificare due volte)?

Grazie

risposta

18
X(?:A()|B()){2}\1\2Y 

In sostanza, si utilizza un gruppo di cattura vuoto per spuntare ogni elemento quando è abbinato, quindi il back-riferimenti in modo che tutto è stato verificato al largo.

essere consapevoli che questo si basa sul comportamento regex privi di documenti, quindi non c'è alcuna garanzia che funzionerà nel vostro sapore regex - e se lo fa, non c'è alcuna garanzia che sarà continuerà a lavorare come che si evolve sapore. Ma per quanto ne so, funziona in ogni sapore che supporta i riferimenti di ritorno. (EDIT: non funziona in JavaScript.)

EDIT: Tu dici che stai usando gruppi denominati per catturare parti della partita, che aggiunge un sacco di confusione visiva al regex, se non reale complessità. Bene, se ti capita di usare regex .NET, puoi comunque usare semplici gruppi numerati per le "caselle di controllo". Ecco un esempio semplicistico che trova e raccoglie a parte un po 'di stringhe mese-giorno senza conoscere il loro ordine interno:

Regex r = new Regex(
    @"(?: 
     (?<MONTH>Jan|Feb|Mar|Apr|May|Jun|Jul|Sep|Oct|Nov|Dec)() 
     | 
     (?<DAY>\d+)() 
    ){2} 
     \1\2", 
    RegexOptions.IgnorePatternWhitespace); 

    string input = @"30Jan Feb12 Mar23 4Apr May09 11Jun"; 
    foreach (Match m in r.Matches(input)) 
    { 
    Console.WriteLine("{0} {1}", m.Groups["MONTH"], m.Groups["DAY"]); 
    } 

Questo funziona perché in .NET, la presenza di gruppi denominati non ha alcun effetto sul l'ordinamento della non gruppi con nome. Ai gruppi con nome vengono assegnati dei numeri, ma i numeri iniziano con dopo l' l'ultimo dei gruppi senza nome. (So ​​che sembra complicato in modo gratuito, ma ci sono buone ragioni per farlo in questo modo.)

Normalmente si desidera evitare l'uso di gruppi di cattura con nome e senza nome, specialmente se si utilizzano i riferimenti di ritorno, ma Penso che questo caso potrebbe essere un'eccezione legittima.

+0

Funziona! Poiché le mie espressioni A e B contengono un gruppo completo di gruppi, ho utilizzato i gruppi di acquisizione vuoti denominati: X (?: A (? ) | B (? )) {2} \ k \ k Y – Jimmy

+0

@Jimmy: vedi la mia modifica sui gruppi nominati. –

+0

Grazie per il suggerimento aggiuntivo. Sto usando .net, ma le mie stringhe A e B contengono anche un gruppo di gruppi di cattura non denominati (quando creo la regex uso RegexOptions.ExplicitCapture). Penso che usando?: In tutti questi gruppi crea più confusione rispetto all'utilizzo di due gruppi "fittizi". Ulteriori commenti benvenuto :) – Jimmy

1

Se vi sono più stringhe, con qualsiasi tipo di caratteri in là, sarete meglio con:

X(.)+Y 
Solo

numeri allora solo

X([0-9])+Y 

lettere

X([a-zA-Z])+Y 

lettere e numeri

X([a-zA-Z][0-9])+Y 
+0

No, A & B deve corrispondere più precisamente di quello; per esempio, la parte 'A' che uso nel mio esempio è in realtà (s = \ s * (? \ d * \.? \ d *) \ s + e la parte 'B' è in realtà r = \ s * (? \ d) (/ (? \ d))?). Ecco perché AB | AB diventa un po 'difficile da mantenere. – Jimmy

3

È possibile memorizzare pezzi regex nelle variabili, e fare:

A=/* relevant regex pattern */ 
B=/* other regex pattern */ 
regex = X($A$B|$B$A)Y 

In questo modo è sufficiente specificare ogni regex una volta, su una riga, che dovrebbe rendere più facile da mantenere.

Sidenote: Stai cercando di trovare le permutazioni, il che è ok visto che stai guardando solo 2 sottressi. Ma se si desidera aggiungere un terzo (o un quarto), le permutazioni regex crescono drasticamente - (abc | acb | bac | bca | cab | cba) - o peggio. Se hai bisogno di seguire la strada delle permutazioni, c'è una buona discussione su questo qui su stackoverflow. È per la permutazione delle lettere, e le soluzioni usano awk/bash/perl, ma almeno questo ti dà un punto di partenza.

+0

Siamo spiacenti, questo non è troppo utile. Io uso un editor regex per testare la regex, e questa è la parte che diventa complessa quando si ripete la regex complessa ... memorizzare la regex in parti nel mio programma non sarebbe di aiuto in questo caso. – Jimmy

1

provare questo

X((A|B){2})Y 
+1

Scusa, non funzionerebbe, non voglio consentire stringhe come AA o BB, solo AB o BA :) – Jimmy

Problemi correlati