Python regex znaleźć wszystkie pokrywające się dopasowania?


102

Próbuję znaleźć każdą 10-cyfrową serię liczb w większej serii liczb, używając funkcji re w Pythonie 2.6.

Z łatwością mogę złapać żadne nakładające się mecze, ale chcę, aby każdy mecz w serii liczbowej. Na przykład.

w „123456789123456789”

Powinienem otrzymać następującą listę:

[1234567891,2345678912,3456789123,4567891234,5678912345,6789123456,7891234567,8912345678,9123456789]

Znalazłem odniesienia do „lookahead”, ale przykłady, które widziałem, pokazują tylko pary liczb, a nie większe grupy i nie byłem w stanie przekonwertować ich poza dwie cyfry.


6
Przedstawione rozwiązania nie zadziałają, gdy pokrywające się dopasowania rozpoczynają się w tym samym punkcie, np. Dopasowanie „a | ab | abc” do „abcd” zwróci tylko jeden wynik. Czy istnieje na to rozwiązanie, które nie wymaga wielokrotnego wywoływania funkcji match () i ręcznego śledzenia granicy „końca”?
Vítor De Araújo

@ VítorDeAraújo: nakładające się wyrażenia regularne, takie jak, (a|ab|abc)mogą być generalnie przepisane jako nienakładające się z zagnieżdżonymi grupami przechwytywania, np. (a(b(c)?)?)?Gdy ignorujemy wszystkie oprócz skrajnej (tj. Lewej) grupy przechwytywania podczas rozpakowywania dopasowania; co prawda jest to trochę bolesne i mniej czytelne. Będzie to również bardziej wydajne wyrażenie regularne.
smci

Odpowiedzi:


181

Użyj grupy przechwytywania wewnątrz lookahead. Lookahead przechwytuje tekst, który Cię interesuje, ale rzeczywiste dopasowanie jest technicznie podciągiem o zerowej szerokości przed lookahead, więc dopasowania technicznie się nie pokrywają:

import re 
s = "123456789123456789"
matches = re.finditer(r'(?=(\d{10}))',s)
results = [int(match.group(1)) for match in matches]
# results: 
# [1234567891,
#  2345678912,
#  3456789123,
#  4567891234,
#  5678912345,
#  6789123456,
#  7891234567,
#  8912345678,
#  9123456789]

2
Moja odpowiedź jest co najmniej 2 razy szybsza niż ta. Ale to rozwiązanie jest trudne, głosuję za nim.
eyquem,

16
Explanation = zamiast szukać wzorca (10 cyfr), wyszukuje wszystko, czego DOTYCZY wzorzec. Znajduje więc pozycję 0 łańcucha, pozycję 1 łańcucha i tak dalej. Następnie chwyta grupę (1) - pasujący wzorzec i tworzy ich listę. Bardzo fajny.
Tal Weiss,

10
Dołączyłem do StackOverflow, odpowiadałem na pytania i poprawiłem swoją reputację, aby móc zagłosować za tą odpowiedzią. Na razie utknąłem w Pythonie 2.4, więc nie mogę korzystać z bardziej zaawansowanych funkcji regex w Pythonie 3, a to jest właśnie rodzaj dziwacznej sztuczki, której szukałem.
TheSoundDefense,

2
Czy mógłbyś dodać więcej wyjaśnień do kodu. Nie jest to najlepszy sposób, jak na przepełnienie stosu, po prostu mieć kod w odpowiedzi. Na pewno pomoże ludziom.
Akshay Hazari

1
To jest naprawdę pomocne. Dzięki: D
Sreekiran

80

Możesz także spróbować użyć modułu innej firmyregex (nie re), który obsługuje nakładające się dopasowania.

>>> import regex as re
>>> s = "123456789123456789"
>>> matches = re.findall(r'\d{10}', s, overlapped=True)
>>> for match in matches: print(match)  # print match
...
1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789

Dostaję:TypeError: findall() got an unexpected keyword argument 'overlapped'
Carsten

@Carsten: najpierw musisz zainstalować regexmoduł:pip install regex
David C

To zadziałało, dzięki. Pomyślałbym, że wystąpi błąd importu, jeśli regex nie jest zainstalowany
Carsten

17

Lubię wyrażenia regularne, ale nie są one tutaj potrzebne.

Po prostu

s =  "123456789123456789"

n = 10
li = [ s[i:i+n] for i in xrange(len(s)-n+1) ]
print '\n'.join(li)

wynik

1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789

10
Regeksy nie są tutaj potrzebne tylko dlatego, że stosujesz specjalną wiedzę „w ramach większej serii liczb”, więc już wiesz, że każda pozycja 0 <= i < len(s)-n+1na pewno będzie początkiem 10-cyfrowego dopasowania. Wydaje mi się również, że twój kod można przyspieszyć, byłoby interesujące kodowanie golfa dla szybkości.
smci
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.