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

18

In einem regulären Python-Ausdruck stoße ich auf dieses singuläre Problem. Könnten Sie Anweisungen zu den Unterschieden zwischen re.findall('(ab|cd)', string)und geben 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)

Die tatsächliche Ausgabe ist:

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

Ich bin verwirrt, warum das zweite Ergebnis nicht 'ab'so gut enthält.

Felsen
quelle
re.findall ('(ab | cd)', string) bekommt ['ab', 'cd'] re.findall ('(ab | cd) +', string) bekommt ['cd']
rock

Antworten:

15

+ist ein Wiederholungsquantifizierer, der ein- oder mehrmals übereinstimmt. In der Regex (ab|cd)+werden Sie Wiederholung der Capture - Gruppe (ab|cd) + mit. Dadurch wird nur die letzte Iteration erfasst.

Sie können über dieses Verhalten wie folgt argumentieren:

Angenommen, Ihre Zeichenfolge ist abcdlaund Regex ist (ab|cd)+. Die Regex-Engine findet eine Übereinstimmung für die Gruppe zwischen den Positionen 0 und 1 als abund verlässt die Erfassungsgruppe. Dann sieht es den +Quantifizierer und versucht so, die Gruppe erneut zu erfassen und wird cdzwischen den Positionen 2 und 3 erfassen .


Wenn Sie alle Iterationen erfassen möchten, sollten Sie stattdessen die sich wiederholende Gruppe erfassen, mit ((ab|cd)+)der abcdund übereinstimmt cd. Sie können die innere Gruppe nicht erfassen, da es uns egal ist, mit ((?:ab|cd)+)welchen Übereinstimmungen die innere Gruppe übereinstimmtabcd

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

Aus den Dokumenten,

Angenommen, Sie möchten einem Tag wie !abc!oder entsprechen !123!. Nur diese beiden sind möglich, und Sie möchten das abcoder erfassen, 123um herauszufinden, welches Tag Sie erhalten haben. Das ist einfach genug: !(abc|123)!wird den Trick machen.

Angenommen, das Tag kann mehrere Sequenzen von abcund 123, wie !abc123!oder enthalten !123abcabc!. Die schnelle und einfache Lösung ist !(abc|123)+!. Dieser reguläre Ausdruck stimmt tatsächlich mit diesen Tags überein. Es entspricht jedoch nicht mehr unserer Anforderung, das Etikett des Tags in der Erfassungsgruppe zu erfassen. Wenn dieser reguläre Ausdruck übereinstimmt !abc123!, wird nur die Erfassungsgruppe gespeichert 123. Wenn es übereinstimmt !123abcabc!, wird nur gespeichert abc.

Shashank V.
quelle
Können Sie einen Link zu einem Dokument erstellen, in dem klargestellt wird, dass + nur die letzte Iteration erfasst, und was ist eine Erfassungsgruppe?
Gulzar
1
@ Gulzar, hat die Antwort aktualisiert. Sie können hier über Capture-Gruppen lesen - regulär-expressions.info
Shashank V
@ Shashank, danke, deine Antwort ist genau das, was ich brauche. Herzlichen Dank
Rock
@rock Bitte akzeptiere die Antwort, wenn sie deine Frage gelöst hat.
Shashank V
Es ist nicht erforderlich, den gesamten regulären Ausdruck mit Klammern zu umgeben. '(?:ab|cd)+'Wird einfach funktionieren.
Dukeling
5

Ich weiß nicht, ob dies die Dinge mehr klären wird, aber versuchen wir uns auf einfache Weise vorzustellen, was unter der Haube passiert. Wir werden zusammenfassen, was mit Match passiert

   # 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

findallPassen Sie den String an und verbrauchen Sie ihn gleichzeitig. Stellen wir uns vor, was mit diesem REGEX passiert '(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']  

Jetzt das gleiche mit '(ab|cd)+'

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

Ich hoffe das klärt die Sache ein bisschen.

Charif DZ
quelle
0

Für mich war der verwirrende Teil die Tatsache, dass

Wenn eine oder mehrere Gruppen im Muster vorhanden sind, geben Sie eine Liste der Gruppen zurück.

docs

Sie erhalten also keine vollständige Übereinstimmung, sondern nur eine Übereinstimmung eines Captures. Wenn Sie diese Gruppe nicht erfassen lassen (re.findall('(?:ab|cd)+', string), wird sie ["abcd"]wie ursprünglich erwartet zurückgegeben

RiaD
quelle
Ich bin