2009-06-03 9 views
8

Czy możliwe jest wykrywanie powtarzających się wzorców liczb z wyrażeniem regularnym?Wyrażenie regularne do wykrywania powtórzeń w ciągu znaków

Na przykład, jeśli miałbym następujący ciąg "034503450345", czy możliwe byłoby dopasowanie powtórzonej sekwencji 0345? Mam przeczucie, że wykracza to poza zakres regex, ale pomyślałem, że zapytam tutaj i tak, czy coś przeoczyłem.

+1

jakim języku/platforma używasz? –

+0

Używam C#. Jedyne, czego potrzebowałem to regex, więc wdrożyłem rozwiązanie RichieHindle i zweryfikowałem je na podstawie moich danych testowych! Wiele się też nauczyłem z doskonale komentowanego regexu Petera Boughtona. Dzięki Wam obojgu! –

+0

@MarkWithers Mam do czynienia z tym samym problemem. Czy możesz być bardziej konkretny i powiedzieć mi coś więcej o swoim rozwiązaniu? Dziękuję – user2179427

Odpowiedz

9

Tak, można - oto przypadek testowy Python

import re 
print re.search(r"(\d+).*\1", "8034503450345").group(1) 
# Prints 0345 

Wyrażenie regularne mówi „znaleźć jakiś ciąg cyfr, a następnie dowolną ilość innych rzeczy, to ta sama sekwencja ponownie.”

Na ledwo powiązana uwaga, oto jeden z moich ulubionych wyrażeń regularnych - głównym detektor numer:

import re 
for i in range(2, 100): 
    if not re.search(r"^(xx+)\1+$", "x"*i): 
     print i 
+0

Twój detektor liczb pierwszych znajduje 0 i 1 jako główny :-) – balpha

+0

Dowolny pomysł dlaczego następujący przykład to * tylko * dopasowanie '8', a nie' 0345'? w [18]: xxx = re.search (R "(\ D +) * \ 1", "+80345824103452420345") w [19]: foo.groups() Z [19]: ('8 ",) –

+0

@balpha: Dobry pont - naprawiony. 8-) – RichieHindle

19

To wyrażenie pasuje jedna lub więcej grup powtarzając:

(.+)(?=\1+) 


Oto to samo wyrażenie w podziale, (używając komentarza, aby można go było używać bezpośrednio jako wyrażenie regularne).

(?x) # enable regex comment mode 
( # start capturing group 
.+ # one or more of any character (excludes newlines by default) 
)  # end capturing group 
(?= # begin lookahead 
\1+ # match one or more of the first capturing group 
)  # end lookahead 


W celu dopasowania do określonego wzoru, zmienia .+ do tego wzoru, np \d+ dla jednego lub więcej numerów lub \d{4,}, aby dopasować 4 lub więcej liczb.

Aby dopasować określoną liczbę wzorów, zmień \1+, np. \1{4} na cztery powtórzenia.

Aby pozwolić powtórzeniu nie być obok siebie, możesz dodać .*? w gnieździe wyprzedzającym.

+1

Doskonałe wyjaśnienie +1 – ichiban

+0

Dobry przykład, bardzo dobrze wyjaśniony –

+0

Doskonałe wyjaśnienie. Doskonałe przedłużenie. Dzięki!! +1 – Toto

8

Wystarczy dodać notatkę do (poprawnej) odpowiedzi z RichieHindle:

pamiętać, że realizacja regexp Pythona (i wiele innych, takich jak Perl) może to zrobić, to już nie jest wyrażeniem regularnym w wąski sens tego słowa.

Twój przykład nie jest zwykłym językiem, dlatego nie może być obsługiwany przez zwykłe wyrażenie regularne. Zobacz np. doskonała Wikipedia article w celu uzyskania szczegółowych informacji.

Podczas gdy jest to w większości interesujący tylko akademickie, istnieją pewne praktyczne konsekwencje. Prawdziwe wyrażenia regularne mogą znacznie lepiej zagwarantować maksymalne czasy działania niż w tym przypadku. Więc w pewnym momencie możesz mieć problemy z wydajnością.

Nie można powiedzieć, że to nie jest dobre rozwiązanie, ale należy zdać sobie sprawę z tego, że jesteś na granicy tego, co potrafią wyrażenia regularne (nawet w rozszerzonej formie), i może warto rozważyć inne rozwiązania w przypadku problemów .

+0

Bardzo ciekawa lektura, dzięki za to. –

2

To jest kod C#, który wykorzystuje konstrukcję odwołania wstecznego w celu znalezienia powtarzających się cyfr. Będzie działać z 034503450345, 123034503450345, 034503450345345, 232034503450345423. Wyjaśnienie jest znacznie łatwiejsze i bardziej zrozumiałe.

/// <summary> 
/// Assigns repeated digits to repeatedDigits, if the digitSequence matches the pattern 
/// </summary> 
/// <returns>true if success, false otherwise</returns> 
public static bool TryGetRepeatedDigits(string digitSequence, out string repeatedDigits) 
{ 
    repeatedDigits = null; 

    string pattern = @"^\d*(?<repeat>\d+)\k<repeat>+\d*$"; 

    if (Regex.IsMatch(digitSequence, pattern)) 
    { 
     Regex r = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); 
     repeatedDigits = r.Match(digitSequence).Result("${repeat}"); 
     return true; 
    } 
    else 
     return false; 
} 
+0

Bardzo ładne! Lubię korzystać z nazwanej grupy. Kod jakości produkcji, skomentowany i gotowy do skopiowania. Dziękuję bardzo! –

+0

"Gotowy do skopiowania": D .. Podoba mi się to !!!! –

0

użyć wyrażenia regularnego powtarzania: bar {2} szuka tekstu z dwoma lub więcej bar: Barbar barbarbar ...

Powiązane problemy