re.findall ('(ab | cd)', string) vs re.findall ('(ab | cd) +', string)

18

W wyrażeniu regularnym Python napotykam ten szczególny problem. Czy możesz podać instrukcje dotyczące różnic między re.findall('(ab|cd)', string)i re.findall('(ab|cd)+', string)?

import re

string = 'abcdla'
result = re.findall('(ab|cd)', string)
result2 = re.findall('(ab|cd)+', string)
print(result)
print(result2)

Rzeczywista wydajność wynosi:

['ab', 'cd']
['cd']

Jestem zdezorientowany, dlaczego drugi wynik również nie zawiera 'ab'?

skała
źródło
re.findall ('(ab | cd)', string) dostaje ['ab', 'cd'] re.findall ('(ab | cd) +', string) dostaje ['cd']
rock

Odpowiedzi:

15

+to powtarzalny kwantyfikator, który pasuje jeden lub więcej razy. W regex (ab|cd)+, jesteś powtarzając grupę przechwytywania (ab|cd) za pomocą +. To uchwyci tylko ostatnią iterację.

Możesz uzasadnić to zachowanie w następujący sposób:

Powiedz, że twój ciąg to abcdlai wyrażenie regularne (ab|cd)+. Silnik Regex znajdzie dopasowanie dla grupy między pozycjami 0 i 1 abi opuszcza grupę przechwytywania. Następnie widzi +kwantyfikator i próbuje ponownie przejąć grupę i przechwyci cdmiędzy pozycjami 2 i 3.


Jeśli chcesz przechwycić wszystkie iteracje, powinieneś przechwycić powtarzającą się grupę, z ((ab|cd)+)którą pasuje abcdi cd. Możesz sprawić, że grupa wewnętrzna nie będzie przechwytywana, ponieważ nie dbamy o mecze grupy wewnętrznej, z ((?:ab|cd)+)którymi meczeabcd

https://www.regular-expressions.info/captureall.html

Z Dokumentów,

Powiedzmy, że chcesz dopasować tag, taki jak !abc!lub !123!. Tylko te dwa są możliwe i chcesz uchwycić abclub 123dowiedzieć się, który tag masz. To dość proste: załatwi !(abc|123)!sprawę.

Powiedzmy teraz, że znacznik może zawierać wiele sekwencji abci 123, takich jak !abc123!lub !123abcabc!. Szybkim i łatwym rozwiązaniem jest !(abc|123)+!. To wyrażenie regularne rzeczywiście pasuje do tych tagów. Jednak nie spełnia już naszego wymogu przechwytywania etykiety znacznika do grupy przechwytywania. Kiedy to wyrażenie regularne pasuje !abc123!, grupa przechwytująca przechowuje tylko 123. Kiedy pasuje !123abcabc!, tylko przechowuje abc.

Shashank V.
źródło
czy możesz połączyć się z jakimś dokumentem, wyjaśniając, że + przechwytuje tylko ostatnią iterację i czym jest grupa przechwytywania?
Gulzar
1
@Gulzar, zaktualizowałem odpowiedź. Możesz przeczytać o grupach przechwytywania tutaj - regular-expressions.info/refcapture.html
Shashank V
@Shashank, dzięki, twoja odpowiedź jest dokładnie tym, czego potrzebuję. szczerze dziękuję
rock
@rock Proszę przyjąć odpowiedź, jeśli rozwiązało to pytanie.
Shashank V
Nie trzeba otaczać całego wyrażenia regularnego nawiasami klamrowymi. Po prostu '(?:ab|cd)+'zadziała.
Dukeling
5

Nie wiem, czy to rozwiąże więcej, ale spróbujmy sobie wyobrazić, co dzieje się pod maską w prosty sposób, podsumujemy to, co się stanie, używając dopasowania

   # group(0) return the matched string the captured groups are returned in groups or you can access them
   # using group(1), group(2).......  in your case there is only one group, one group will capture only 
   # one part so when you do this
   string = 'abcdla'
   print(re.match('(ab|cd)', string).group(0))  # only 'ab' is matched and the group will capture 'ab'
   print(re.match('(ab|cd)+', string).group(0)) # this will match 'abcd'  the group will capture only this part 'cd' the last iteration

findalldopasuj i zużyj ciąg w tym samym czasie wyobraźmy sobie, co się stanie z REGEX '(ab|cd)':

      'abcdabla' ---> 1:   match: 'ab' |  capture : ab  | left to process:  'cdabla'
      'cdabla'   ---> 2:   match: 'cd' |  capture : cd  | left to process:  'abla'
      'abla'     ---> 3:   match: 'ab' |  capture : ab  | left to process:  'la'
      'la'       ---> 4:   match: '' |  capture : None  | left to process:  ''

      --- final : result captured ['ab', 'cd', 'ab']  

Teraz to samo z '(ab|cd)+'

      'abcdabla' ---> 1:   match: 'abcdab' |  capture : 'ab'  | left to process:  'la'
      'la'       ---> 2:   match: '' |  capture : None  | left to process:  ''
      ---> final result :   ['ab']  

Mam nadzieję, że to trochę wyjaśni.

Charif DZ
źródło
0

Więc dla mnie mylące było to, że

Jeśli we wzorcu występuje jedna lub więcej grup, zwróć listę grup;

dokumenty

więc nie zwraca pełnego dopasowania, a jedynie dopasowanie do przechwytywania. Jeśli (re.findall('(?:ab|cd)+', string)sprawisz , że ta grupa nie będzie przechwytywać , wróci, ["abcd"]jak początkowo oczekiwałem

RiaD
źródło
nie jestem pewien, czego się spodziewałeś, czy nie
RiaD