Regex optionale Wortübereinstimmung

73

Ich versuche, eine Regex zu erstellen, um Sänger und Texter zu extrahieren. Ich habe mich gefragt, wie ich die Suche nach Textern optional machen kann.

Beispiel für eine mehrzeilige Zeichenfolge:

Fireworks Singer: Katy Perry
Vogue Singers: Madonna, Karen Lyricist: Madonna

Regex: /Singers?:(.\*)\s?Lyricists?:(.\*)/

Dies stimmt korrekt mit der zweiten Zeile überein und extrahiert Singers(Madonna, Karen)undLyricists(Madonna)

Aber es funktioniert nicht mit der ersten Zeile, wenn es keine Texter gibt.

Wie mache ich die Suche nach Textern optional?

Sieger
quelle

Antworten:

114

Sie können den Teil, den Sie abgleichen möchten, in eine nicht erfassende Gruppe einschließen : (?:). Dann kann es als einzelne Einheit in der Regex behandelt werden, und anschließend können Sie eine nachfolgende Einheit einfügen, ?um es optional zu machen. Beispiel:

/Singers?:(.*)\s?(?:Lyricists?:(.*))?/

Beachten Sie, dass hier das \s?nutzlos ist, da .*alle Charaktere gierig gefressen werden und kein Zurückverfolgen erforderlich ist. Dies bedeutet auch, dass das (?:Lyricists?:(.*))Teil aus demselben Grund niemals abgeglichen wird. Sie können die nicht-gierige Version verwenden .*, .*?zusammen mit der $dieses Problem zu beheben:

/Singers?:(.*?)\s*(?:Lyricists?:(.*))?$/

Einige zusätzliche Leerzeichen werden erfasst. Dies kann auch entfernt werden, wodurch eine endgültige Regex von:

/Singers?:\s*(.*?)\s*(?:Lyricists?:\s*(.*))?$/
Cameron
quelle
Wie würde dies bei Verwendung der übereinstimmenden Muster ( $1, $2...) funktionieren , würden verschachtelte Klammern auch über eine Nummer referenziert?
Puk
@puk: Ich bin nicht sicher, ob ich deine Frage verstehe. (?:)ist eine nicht erfassende Gruppe, was bedeutet, dass alles, worüber sie übereinstimmen, nicht referenziert werden $1kann usw. ()ist eine erfassende Gruppe, was bedeutet, dass alles, was sie übereinstimmen, erfasst und verfügbar $1ist usw. Wenn Sie verschachtelte (())Gruppen haben, ist die äußerste $1und die innere wird $2usw. sein (dies folgt immer noch der normalen Regel von links nach rechts; die öffnenden (Klammern bestimmen, welche Gruppe welche Nummer erhält).
Cameron
Diese Art der Beantwortung meiner Frage wollte ich über so etwas wie (([a-z]{32})?):(([0-9]{32})?)einen optionalen Benutzernamen wissen : id double. Wenn ich das auf der Zeichenfolge laufen lasse, ":"sind beide $1und $2leer? Was ist mit "john:"? Gibt es ein $3und ein $4?
Puk
1
@puk: Hmm, mal sehen. Der beste und schnellste Weg wäre, dies durch einen Regex-Tester durchzuführen (oder Ihre eigenen Tests mit Ihrer spezifischen Regex-Engine zu schreiben). Aber lassen Sie mich einen Blick darauf werfen. Sie haben vier Gruppen. Für ':' haben die Gruppen 1 und 3 einen Wert für die leere Zeichenfolge, da sie mit allem in ihnen übereinstimmen (dh optionalem Material). Die Gruppen 2 und 4 (gefolgt von den Gruppen ?) sind je nach Sprache / Regex-Engine undefiniert / null / leer. Sie können den regulären Ausdruck in zwei Gruppen vereinfachen: ([a-z]{32})?:([0-9]{32})?oder nicht erfassende Gruppen verwenden : ((?:[a-z]{32})?):((?:[0-9]{32})?).
Cameron
1
re.compile (r '(. *) (?: SKIPPED?) (?: PASSED?) (?: FAILED?)') Kann ich den obigen Ausdruck verwenden, um optional nach SKIPPED /PASSED/Failed.....und zu suchen einer von ihnen muss anwesend sein
Ravi Yadav
0

Nur um Camerons Lösung zu ergänzen. Wenn die Quellzeichenfolge mehrere Zeilen enthält, die jeweils sowohl Sänger als auch Texter enthalten, müssen Sie wahrscheinlich den mehrzeiligen Modifikator 'm' hinzufügen, damit das '$' mit den Zeilenenden übereinstimmt. (Sie haben nicht angegeben, welche Sprache Sie verwenden. Möglicherweise möchten Sie auch den Modifikator 'i' hinzufügen.)

Ridgerunner
quelle