2009-02-26 13 views
27

Esiste un modo per specificare un'espressione regolare che corrisponda ad ogni seconda occorrenza di un motivo in una stringa?Corrispondenza ad ogni secondo occorrenza

Esempi

  • ricerca di un contro stringa abcdabcd dovrebbe trovare un'occorrenza in posizione 5
  • ricerca di ab contro stringa abcdabcd dovrebbe trovare un'occorrenza in posizione 5
  • alla ricerca di dab contro stringa abcdabcd dovrebbe trovare nessun occorrenze
  • alla ricerca di un contro stringa aaaa dovrebbe trovare due ripetizioni nelle posizioni 2 e 4
+1

Forse sono troppo schizzinoso ma un regex non "trova" nulla. Sarà solo "abbinare" una parte della stringa di input. È il tuo linguaggio di programmazione che ti offre funzioni per abbinare una stringa a una regex e restituire varie informazioni sulla partita (ad esempio dove si è verificato). –

+16

hai assolutamente ragione, siete troppo esigente;) –

risposta

46

Utilizzare il raggruppamento.

foo.*?(foo) 
2

Sarebbe qualcosa di simile

(pattern.\*?(pattern))* 

lavoro per voi?

Edit:

Il problema di questo è che è utilizza il non-avido operatore * ?, e può essere necessario un sacco di backtracking lungo la corda, mentre espressioni regolari di solito non hanno a guardare una lettera più di una volta. Ciò che questo significa per te, è che questo potrebbe essere lento per grandi lacune.

+0

ha bisogno di essere non-avido – annakata

+0

dimenticato. Aggiustato. – Patrick

+1

Non sono sicuro, Patrick, direi che gli operatori non avidi possono utilizzare meno backtracking. Dipende l'algoritmo utilizzato, naturalmente, ma per controllare "a. * Un" devi andare fino alla fine della stringa e cercare di corrispondenza arretrata, per "un. *? Un" si può provare corrispondenti in avanti e fermati quando lo fai –

8

Supponiamo che il modello desiderato sia abc + d. Vuoi abbinare la seconda occorrenza di questo modello in una stringa.

Si potrebbe costruire la seguente espressione regolare:

abc+d.*?(abc+d) 

Questo dovrebbe corrispondere le stringhe del modulo: <your-pattern>...<your-pattern>. Dal momento che stiamo usando il qualificatore riluttante *? siamo sicuri che non ci possa essere un'altra corrispondenza tra i due. Usando i gruppi di corrispondenza che praticamente tutte le implementazioni di espressioni regolari forniscono, si recupererebbe la stringa nel gruppo a parentesi, che è ciò che si desidera.

0

Non c'è alcun modo "diretto" di farlo, ma è possibile specificare il modello due volte più in: a[^a]*a che corrispondono fino al secondo "a".

L'alternativa è usare il linguaggio di programmazione (perl? C#? ...) per far corrispondere la prima occorrenza e poi la seconda.

EDIT: Ho visto altri rispondere utilizzando gli operatori "non-golosi" che potrebbero essere un buon modo per andare, assumendo che li abbiate nella vostra libreria regex!

+1

/a [^ a] * a/ritrova i prossimi due occorrenze di 'a', ma non vi dico dove la seconda è. Inoltre, funziona solo quando il pattern base è esattamente lungo un carattere. –

6

Se si utilizza C#, è possibile ottenere tutte le corrispondenze contemporaneamente, ad es. usa Regex.Matches() che restituisce un MatchCollection (controlla l'indice dell'articolo, indice% 2! = 0).

Se si vuole trovare il verificarsi di sostituirlo, utilizzare uno dei sovraccarichi di Regex.Replace() che utilizzano un MatchEvaluator), ad esempio, Regex.Replace (String, String, MatchEvaluator, ecco il codice:.

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Text.RegularExpressions; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string input = "abcdabcd"; 

      // Replace *second* a with m 

      string replacedString = Regex.Replace(
       input, 
       "a", 
       new SecondOccuranceFinder("m").MatchEvaluator); 

      Console.WriteLine(replacedString); 
      Console.Read(); 

     } 

     class SecondOccuranceFinder 
     { 
      public SecondOccuranceFinder(string replaceWith) 
      { 
       _replaceWith = replaceWith; 
       _matchEvaluator = new MatchEvaluator(IsSecondOccurance); 
      } 

      private string _replaceWith; 

      private MatchEvaluator _matchEvaluator; 
      public MatchEvaluator MatchEvaluator 
      { 
       get 
       { 
        return _matchEvaluator; 
       } 
      } 

      private int _matchIndex; 
      public string IsSecondOccurance(Match m) 
      { 
       _matchIndex++; 
       if (_matchIndex % 2 == 0) 
        return _replaceWith; 
       else 
        return m.Value; 
      } 
     } 
    } 
} 
2

riferimenti posteriori possono trovare soluzioni interessanti qui Questo espressione regolare:

([a-z]+).*(\1) 

troverà la sequenza più lunga ripetuto

. Questo troverà una sequenza di 3 lettere che viene ripetuta:

([a-z]{3}).*(\1) 
+1

Questo problema è leggermente diverso rispetto alle altre risposte, ma è comunque necessario rendere il quantificatore non-avido: /([a-z]+).*?(\1)/ –

Problemi correlati