2009-02-26 10 views
27

Czy istnieje sposób określenia wyrażenia regularnego pasującego do każdego drugiego wystąpienia wzorca w ciągu znaków?Dopasowywanie co sekundę

Przykłady

  • szukając przeciwko ciąg abcdabcd powinny znaleźć jedno wystąpienie w pozycji 5
  • szukasz ab przeciwko ciąg abcdabcd powinny znaleźć jedno wystąpienie w pozycji 5
  • szukając dab przeciwko ciągowi abcdabcd powinien znaleźć żadnych wystąpień
  • szukając przeciwko strun aaaa powinny znaleźć dwa wystąpienia w pozycjach 2 i 4
+1

Może jestem zbyt wybredna, ale regex nie "znajdzie" niczego. Będzie tylko "dopasowywać" część twojego wejściowego łańcucha. To twój język programowania oferuje funkcje, które dopasowują ciąg do wyrażenia regularnego i zwracają różne informacje o dopasowaniu (np. Gdzie się pojawił). –

+16

masz absolutną rację, jesteś zbyt wybredna;) –

Odpowiedz

46

Użyj grupowania.

foo.*?(foo) 
2

Would coś podobnego

(pattern.\*?(pattern))* 

pracy dla Ciebie?

Edit:

Problem z tym jest to, że używa non-chciwy napędu * ?, a to może być wymagają bardzo dużo backtracking wzdłuż łańcucha, natomiast Wyrażenia regularne zwykle nie trzeba patrzeć na list więcej niż jeden raz. Dla ciebie oznacza to, że może to być powolne w przypadku dużych luk.

+0

musi być nie-chciwa – annakata

+0

Zapomniałem o tym. Naprawione. – Patrick

+1

Nie jestem pewien, Patrick, powiedziałbym, że nieagaciści operatorzy mogą użyć mniejszego cofnięcia. Zależy oczywiście od algorytmu, którego używasz, ale aby sprawdzić "a. * A" musisz iść do końca łańcucha i spróbować dopasować do tyłu, dla "a. *? A" możesz spróbować dopasować do przodu i zatrzymaj się, kiedy to zrobisz. –

8

Załóżmy, że pożądany wzór to abc + d. Chcesz dopasować drugie wystąpienie tego wzorca w ciągu znaków.

Można by skonstruować następujące wyrażenia regularnego:

abc+d.*?(abc+d) 

by to dopasować ciągi postaci: <your-pattern>...<your-pattern>. Skoro używamy niechętnego kwalifikatora *? jesteśmy bezpieczni, że nie może być kolejnego meczu pomiędzy nimi. Używanie grup matcherów, które zapewniają prawie wszystkie implementacje regex, spowoduje pobranie łańcucha w grupie w nawiasach, co jest tym, czego potrzebujesz.

0

Nie ma „bezpośredni” sposób robić to, ale można określić wzór dwukrotnie w: a[^a]*a który odnosi się do drugiej „a”.

Alternatywą jest użycie języka programowania (perl? C#? ...) w celu dopasowania do pierwszego wystąpienia, a następnie drugiego.

EDIT: Widziałem inny odpowiedział pomocą „non-chciwych” podmioty, które mogą być dobrym sposobem, aby przejść, zakładając, że masz je w bibliotece regex!

+1

/a [^ a] * a/znajduje następne dwa wystąpienia znaku "a", ale nie informuje o miejscu drugiego. Ponadto działa tylko wtedy, gdy wzór podstawowy ma dokładnie jeden znak. –

6

Jeśli używasz C#, możesz uzyskać wszystkie dopasowania naraz, np. użyj Regex.Matches(), która zwraca MatchCollection (sprawdź indeks elementu, indeks% 2! = 0).

Jeśli chcesz znaleźć zdarzenie do zastąpienia go, użyj jednego z przeciążeń Regex.Replace(), które używają MatchEvaluator), np. Regex.Replace (String, String, MatchEvaluator, oto kod:.

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

Powrót referencje można znaleźć ciekawe rozwiązania tutaj ten regex:

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

znajdzie najdłuższy powtarzana sekwencja

. Ten znajdzie sekwencję 3 liter, która się powtarza:

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

To jest nieco inne podejście do problemu niż inne odpowiedzi, ale nadal musisz uczynić kwantyfikator nie chciwy: /([a-z]+).*?(\1)/ –

Powiązane problemy