Regex genau n ODER m mal

105

Betrachten Sie den folgenden regulären Ausdruck, in dem Xsich ein regulärer Ausdruck befindet .

X{n}|X{m}

Diese Regex würde testen, Xob genau n oderm Zeiten.

Gibt es einen Regex-Quantifizierer, der Xgenau noder zu bestimmten mZeiten auf ein Vorkommen testen kann ?

FThompson
quelle
Nein. Zwei Vorkommen von Xist das Beste, was Sie für allgemein bekommen mkönnen n.
John Dvorak
Wenn dies mein Problem wäre, würde ich Regex-Rückreferenzen ausprobieren und mit beginnen (X)\1{n-1}(?:\1{m-n-1}). Ich weiß, dass dies Xmindestens einmal übereinstimmt, aber um zu beginnen, probieren Sie diese einfache Sache aus und verfeinern Sie sie, indem Sie stattdessen Lookaheads oder Lookbehinds verwenden (X).
Nalply

Antworten:

91

Es gibt keinen einzelnen Quantifizierer, der "genau m oder n Mal" bedeutet. Die Art und Weise, wie Sie es tun, ist in Ordnung.

Eine Alternative ist:

X{m}(X{k})?

wo m < nund kist der Wert von n-m.

Mark Byers
quelle
67

Hier ist die vollständige Liste der Quantifizierer (siehe http://www.regular-expressions.info/reference.html ):

  • ?, ??- 0 oder 1 Vorkommen ( ??ist faul, ?ist gierig)
  • *, *?- beliebig viele Vorkommen
  • +, +?- mindestens ein Vorkommen
  • {n}- genau nVorkommen
  • {n,m}- nzu mEreignissen, einschließlich
  • {n,m}?- nzu mEreignissen, faul
  • {n,}, {n,}?- zumindest nVorkommen

Um "genau N oder M" zu erhalten, müssen Sie den quantifizierten regulären Ausdruck zweimal schreiben, es sei denn, m, n sind speziell:

  • X{n,m} wenn m = n+1
  • (?:X{n}){1,2} wenn m = 2n
  • ...
John Dvorak
quelle
1
Warum wird das ?:im if- m = 2nBeispiel benötigt? Scheint ohne es gut für mich zu funktionieren.
Erb
7
@erb Wenn Sie weglassen ?:, wird die Gruppe zu einer Erfassungsgruppe. Abgesehen davon, dass sich die Regex-Engine an Dinge erinnert, die sie nicht benötigt, ändern sich ihre IDs, wenn Sie nach dieser Gruppe Gruppen erfassen. Wenn Sie Ihren regulären Ausdruck als Ersatz verwenden, müssen Sie den Ersatz anpassen.
John Dvorak
3

TLDR; (?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

Sieht so aus, als ob Sie "xn-mal" oder "xm-mal" möchten. Ich denke, eine wörtliche Übersetzung in Regex wäre (x{n}|x{m}). wie folgt: https://regex101.com/r/vH7yL5/1

oder in einem Fall, in dem Sie eine Folge von mehr als m "x" haben können (unter der Annahme von m> n), können Sie "nach keinem" x "und" gefolgt von keinem "x" hinzufügen, was übersetzt bedeutet, [^x](x{n}|x{m})[^x]aber das würde Nehmen Sie an, dass sich hinter und nach "x" immer ein Zeichen befindet. Wie Sie hier sehen können: https://regex101.com/r/bB2vH2/1

Sie können es in (?:[^x]|^)(x{n}|x{m})(?:[^x]|$)"nach keinem 'x' oder nach dem Zeilenanfang" und "gefolgt von keinem 'x' oder gefolgt vom Zeilenende" ändern . Es werden jedoch nicht zwei Sequenzen mit nur einem Zeichen zwischen ihnen abgeglichen (da für die erste Übereinstimmung ein Zeichen danach und für die zweite ein Zeichen vorher erforderlich wäre), wie Sie hier sehen können: https://regex101.com/r/ oC5oJ4 / 1

Um die Übereinstimmung mit einem Zeichen in der Ferne zu finden, können Sie einen positiven Blick nach vorne (? =) Auf das "no 'x' after" oder einen positiven Blick nach hinten (? <=) Auf das "no 'x' before" hinzufügen. wie folgt: https://regex101.com/r/mC4uX3/1

(?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

Auf diese Weise stimmen Sie nur mit der genauen Anzahl der gewünschten 'x' überein.

Verbessert
quelle
1

Wenn sie sich Enhardeneds Antwort ansehen, stellen sie fest, dass ihr vorletzter Ausdruck nicht mit Sequenzen mit nur einem Zeichen zwischen ihnen übereinstimmt. Es gibt eine einfache Möglichkeit, dies zu beheben, ohne nach vorne / nach hinten schauen zu müssen. Dabei wird das Start- / Endzeichen durch das Begrenzungszeichen ersetzt. Auf diese Weise können Sie mit Wortgrenzen abgleichen, einschließlich Start / Ende. Als solches sollte der geeignete Ausdruck sein:

(?:[^x]|\b)(x{n}|x{m})(?:[^x]|\b)

Wie Sie hier sehen können: https://regex101.com/r/oC5oJ4/2 .

rozza2058
quelle
1
Cool, ich war nicht vertraut damit, wie Regex mit Grenzen umgeht. Das einzige Problem bei dieser Methode ist, wenn Sie eine nicht standardmäßige Grenze verwenden. Erzählen Sie
Enhardened
1
@Enhardened - das ist ein guter Punkt, scheint ein Problem mit mehreren übereinstimmenden Gruppen zu sein, die sich überschneiden. Dies ist eine Situation, in der Sie einen Blick nach hinten verwenden müssen.
Rozza2058
1

Sehr alter Beitrag, aber ich möchte etwas beitragen, das hilfreich sein könnte. Ich habe es genau so versucht, wie es in der Frage angegeben ist, und es funktioniert, aber es gibt einen Haken: Die Reihenfolge der Mengen ist wichtig. Bedenken Sie:

#[a-f0-9]{6}|#[a-f0-9]{3}

Dadurch werden alle Vorkommen von Hex-Farbcodes gefunden (sie sind entweder 3 oder 6 Stellen lang). Aber wenn ich es so umdrehe

#[a-f0-9]{3}|#[a-f0-9]{6}

Es werden nur die 3-stelligen oder die ersten 3-stelligen der 6-stelligen gefunden. Dies ist zwar sinnvoll und ein Regex-Profi kann dies sofort erkennen, für viele ist dies jedoch ein eigenartiges Verhalten. Es gibt einige erweiterte Regex-Funktionen, die diese Falle unabhängig von der Reihenfolge vermeiden können, aber nicht jeder ist knietief in Regex-Mustern.

DanDan
quelle