Regex bis aber nicht inklusive

80

Was ist für Regex die Syntax für die Suche bis, aber nicht eingeschlossen? In etwa wie:

Haystack:
The quick red fox jumped over the lazy brown dog

Expression:
.*?quick -> and then everything until it hits the letter "z" but do not include z
Nudel des Todes
quelle

Antworten:

158

Die explizite Art zu sagen "suchen bis Xaber nicht einschließen X" ist:

(?:(?!X).)*

wo Xkann ein regulärer Ausdruck sein.

In Ihrem Fall könnte dies jedoch übertrieben sein - hier wäre der einfachste Weg

[^z]*

Dies passt zu allem außer zund hört daher kurz vor dem nächsten auf z.

Also .*?quick[^z]*wird passen The quick fox jumps over the la.

Sobald Sie jedoch mehr als einen einfachen Buchstaben haben, auf den Sie achten müssen, (?:(?!X).)*kommt er zum Beispiel ins Spiel

(?:(?!lazy).)*- alles bis zum Wortanfang abgleichen lazy.

Hierbei wird eine Lookahead-Behauptung verwendet , insbesondere ein negativer Lookahead.

.*?quick(?:(?!lazy).)*wird übereinstimmen The quick fox jumps over the.

Erläuterung:

(?:        # Match the following but do not capture it:
 (?!lazy)  # (first assert that it's not possible to match "lazy" here
 .         # then match any character
)*         # end of group, zero or more repetitions.

Wenn Sie nach Schlüsselwörtern suchen, möchten Sie diese möglicherweise mit Wortbegrenzungsankern umgeben: Sie \bfox\bstimmen nur mit dem vollständigen Wort überein fox, nicht jedoch mit dem Fuchs in foxy.

Hinweis

Wenn der abzugleichende Text auch Zeilenumbrüche enthalten kann, müssen Sie die Option "Punkt stimmt mit allen überein" Ihrer Regex-Engine festlegen. Normalerweise können Sie dies erreichen, indem Sie dem regulären Ausdruck voranstellen. Dies (?s)funktioniert jedoch nicht in allen regulären Ausdrucksmodulen (insbesondere JavaScript).

Alternative Lösung:

In vielen Fällen können Sie auch eine einfachere, besser lesbare Lösung verwenden, die einen Lazy Quantifier verwendet. Durch Hinzufügen von a ?zum *Quantifizierer wird versucht, so wenige Zeichen wie möglich von der aktuellen Position abzugleichen:

.*?(?=(?:X)|$)

stimmt mit einer beliebigen Anzahl von Zeichen überein und stoppt unmittelbar vor X(dies kann ein beliebiger regulärer Ausdruck sein) oder dem Ende der Zeichenfolge (falls Xdies nicht übereinstimmt). Möglicherweise müssen Sie auch die Option "Punkt stimmt mit allen überein" festlegen, damit dies funktioniert. (Hinweis: Ich habe eine nicht erfassende Gruppe hinzugefügt, um Xsie zuverlässig vom Wechsel zu isolieren.)

Tim Pietzcker
quelle
+1 Wirklich nette Antwort, funktioniert leider nicht grep, aber diese Antwort funktioniert.
Alexandre Lavoie
@AlexandreLavoie: Interessant. Warum sollte der andere funktionieren und nicht dieser? Beide verwenden Lookahead-Behauptungen. Vielleicht liegt es nur an der (?:...)nicht erfassenden Gruppe? Funktioniert es mit ((?!X).)*?
Tim Pietzcker
1
Ich weiß es wirklich nicht, ich bin weder ein Regex-Experte noch ein Grep. Ich habe verwendet grep, um Anforderungen für nur eine Datenbank aus MySQL Bin Transformet in SQL zu filtern. Hier ist das Biest:grep -Po "(?s)use database_to_keep(.*?)(?=^use)" mysql-bin.000045.sql > filtered.sql
Alexandre Lavoie
Sieht aus wie ein Bash-Konflikt, denn wenn ich die UpTaste drücke, ist der letzte Befehl nicht der, den ich verwendet habe:grep -Po "(?s)use database_to_keep(.*?)(?:(?!^use).)*" mysql-bin.000045.sql > filtered.sql
Alexandre Lavoie
1
Gute Bearbeitung, @ Tim, fügen Sie einfach eine $Alternative hinzu: Ersetzen Sie .*?(?=X)durch.*?(?=X|$)
Wiktor Stribiżew
15

Eine Lookahead-Regex-Syntax kann Ihnen helfen, Ihr Ziel zu erreichen. Somit ist eine Regex für Ihr Beispiel

.*?quick.*?(?=z)

Und es ist wichtig , das zu bemerken , .*?faul Matching vor dem (?=z)Look - Ahead: der Ausdruck paßt einen Teil , bis ein erstes Auftreten des zBriefes.

Hier ist ein C # -Codebeispiel:

const string text = "The quick red fox jumped over the lazy brown dogz";

string lazy = new Regex(".*?quick.*?(?=z)").Match(text).Value;
Console.WriteLine(lazy); // The quick red fox jumped over the la

string greedy = new Regex(".*?quick.*(?=z)").Match(text).Value;
Console.WriteLine(greedy); // The quick red fox jumped over the lazy brown dog
Igor Kustov
quelle
0

Versuche dies

(.*?quick.*?)z
Max
quelle
3
Dies schließt das "z" in das Match ein, genau das möchte der Fragesteller vermeiden. Vielleicht soll der Regex ein Begriff in einem '|' sein alternativ, und dieser alternative reguläre Ausdruck wird verwendet, um mehrere Übereinstimmungen durchzuführen. Wenn das "z" der Anfang einer Zeichenfolge ist, die alternativ mit einem anderen Begriff übereinstimmen würde, verfällt diese Übereinstimmung, da das "z" bereits von der aktuellen Übereinstimmung verbraucht wird.
Szczepan Hołyszewski