Regex - wie man alles außer einem bestimmten Muster zusammenbringt

171

Wie schreibe ich einen regulären Ausdruck, der mit einer Zeichenfolge übereinstimmt, die einem bestimmten Muster nicht entspricht? Ich bin mit einer Situation konfrontiert, in der ich einem (A und ~ B) Muster entsprechen muss.

nicht nicht
quelle
PCRE ist hierfür am besten geeignet : siehe Übereinstimmendes Regex-Muster, ausgenommen, wenn… / Außer dazwischen . Ich habe das findstrTag entfernt, da alle Antworten hier nicht für das Tag gültig sind.
Wiktor Stribiżew

Antworten:

192

Sie könnten eine Vorausschau-Behauptung verwenden:

(?!999)\d{3}

Dieses Beispiel entspricht drei anderen Ziffern als 999.


Wenn Sie jedoch mit dieser Funktion keine Implementierung für reguläre Ausdrücke haben (siehe Vergleich der Geschmacksrichtungen für reguläre Ausdrücke ), müssen Sie wahrscheinlich selbst einen regulären Ausdruck mit den grundlegenden Funktionen erstellen.

Ein kompatibler regulärer Ausdruck mit nur grundlegender Syntax wäre:

[0-8]\d\d|\d[0-8]\d|\d\d[0-8]

Dies stimmt auch mit einer beliebigen dreistelligen Sequenz überein, die dies nicht ist 999.

Gumbo
quelle
1
Look-Ahead ist keine Standardsyntax für reguläre Ausdrücke, sondern eine Perl-Erweiterung. Sie funktioniert nur in Perl-, PCRE- (Perl-kompatiblen RegEx) oder anderen nicht standardmäßigen Implementierungen
Juliano
10
Es ist vielleicht nicht Standard, aber unterstützen die meisten modernen Sprachen es nicht? Welche Sprache unterstützt heutzutage keine Vorausschau?
Bryan Oakley
1
Das ist richtig. Die meisten Regex- Varianten unterstützen diese Funktion (siehe < reguläre-Ausdrücke.info/refflavors.html> ).
Gumbo
1
Ich denke, die letzte Regex würde auch nicht mit 009, 019 ... usw. übereinstimmen
Sebastian Viereck
1
Standard Lex für C verwendet keine PCREs :-(
pieman72
30

Wenn Sie ein Wort A in einer Zeichenfolge und kein Wort B abgleichen möchten. Beispiel: Wenn Sie einen Text haben:

1. I have a two pets - dog and a cat
2. I have a pet - dog

Wenn Sie nach Textzeilen suchen möchten, die einen Hund für ein Haustier haben und KEINE Katze haben , können Sie diesen regulären Ausdruck verwenden:

^(?=.*?\bdog\b)((?!cat).)*$

Es wird nur die zweite Zeile gefunden:

2. I have a pet - dog
Aleks
quelle
Er hat es in der Frage nicht erwähnt, aber das OP verwendet tatsächlich den DOS- findstrBefehl. Es bietet nur einen winzigen Teil der Funktionen, die Sie in einem Regex-Tool erwarten. Lookahead gehört nicht dazu. (Ich habe gerade das findstr- Tag selbst hinzugefügt .)
Alan Moore
2
hm, ja, ich habe jetzt in einem seiner Kommentare zu den Beiträgen gefunden. Ich habe Regex im Titel gesehen. Wie auch immer, wenn jemand diesen Beitrag findet, wenn er wie ich nach regulären Ausdrücken sucht, wie ich es getan habe, könnte er vielleicht für jemanden hilfreich sein :) Danke für die Kommentare
Aleks
15

Übereinstimmung mit dem Muster und Verwendung der Hostsprache, um das boolesche Ergebnis der Übereinstimmung zu invertieren. Dies ist viel besser lesbar und wartbar.

Ben S.
quelle
1
Dann habe ich einfach (~ A oder B) anstelle von (A und ~ B). Es löst mein Problem nicht.
Nicht
1
Pseudocode: String toTest; if (toTest.matches (A) UND! toTest.matches (B)) {...}
Ben S
Ich hätte klarer sein sollen - die Stücke sind nicht völlig unabhängig. Wenn A mit einem Teil der Zeichenfolge übereinstimmt, ist es uns wichtig, ob ~ B mit dem Rest übereinstimmt (aber nicht unbedingt mit der ganzen Sache). Dies war für die Windows-Befehlszeilenfunktion findstr, die ich gefunden habe und die auf echte Regexs beschränkt ist.
Nicht
8

nicht, diese alte Frage wiederzubeleben, weil sie eine einfache Lösung hatte, die nicht erwähnt wurde. (Hab deine Frage gefunden, als du nach einer Regex-Kopfgeld-Quest gesucht hast .)

Ich bin mit einer Situation konfrontiert, in der ich einem (A und ~ B) Muster entsprechen muss.

Die grundlegende Regex dafür ist erschreckend einfach: B|(A)

Sie ignorieren einfach die Gesamtübereinstimmungen und untersuchen die Erfassungen der Gruppe 1, die A enthalten.

Ein Beispiel (mit allen Haftungsausschlüssen zum Parsen von HTML in Regex): A sind Ziffern, B sind Ziffern innerhalb <a tag

Die Regex: <a.*?<\/a>|(\d+)

Demo (siehe Gruppe 1 im unteren rechten Bereich)

Referenz

So passen Sie das Muster an, außer in Situationen s1, s2, s3

So passen Sie ein Muster an, es sei denn ...

zx81
quelle
Das klingt zu schön um wahr zu sein! Leider ist diese Lösung nicht universell und schlägt in Emacs auch nach dem Ersetzen \ddurch fehl [[:digit:]]. In der ersten Referenz wird erwähnt, dass es spezifisch für Perl und PHP ist: "Es gibt eine Variation unter Verwendung der für Perl und PHP spezifischen Syntax, die dasselbe erreicht."
Miguelmorin
4

Die Ergänzung einer regulären Sprache ist auch eine reguläre Sprache. Um sie zu erstellen, müssen Sie jedoch den DFA für die reguläre Sprache erstellen und jede gültige Statusänderung in einen Fehler umwandeln. Sehen Sie dies für ein Beispiel. Was die Seite nicht sagt, ist, dass sie konvertiert /(ac|bd)/wurde /(a[^c]?|b[^d]?|[^ab])/. Die Konvertierung von einem DFA zurück in einen regulären Ausdruck ist nicht trivial. Es ist einfacher, wenn Sie den regulären Ausdruck unverändert verwenden und die Semantik im Code wie zuvor vorgeschlagen ändern können.

Juliano
quelle
2
Wenn ich mich mit tatsächlichen Regex beschäftigen würde, wäre das alles umstritten. Regex scheint sich nun auf den nebulösen CSG-ish (?) Raum des Pattern Matching zu beziehen, den die meisten Sprachen unterstützen. Da ich übereinstimmen muss (A und ~ B), gibt es keine Möglichkeit, die Negation zu entfernen und trotzdem alles in einem Schritt zu erledigen.
Notnot
Lookahead hätte es, wie oben beschrieben, getan, wenn findstr etwas getan hätte, das über echte DFA-Regex hinausgeht. Das Ganze ist irgendwie seltsam und ich weiß nicht, warum ich diesen Befehlszeilenstil (jetzt Batch) ausführen muss. Es ist nur ein weiteres Beispiel dafür, wie mir die Hände gebunden werden.
Notnot
1
@notnot: Sie verwenden findstr von Windows? Dann brauchst du nur noch / v. Wie: findstr Eine Eingabedatei | findstr / v B> outputfile.txt Die erste entspricht allen Zeilen mit A, die zweite entspricht allen Zeilen ohne B.
Juliano
Vielen Dank! Genau das habe ich gebraucht. Ich habe die Frage jedoch nicht so gestellt, also gebe ich Gumbo immer noch die Antwort für die allgemeinere Antwort.
Nicht
1

Muster - re

str.split(/re/g) 

gibt alles außer dem Muster zurück.

testen Sie hier

unigogo
quelle
Sie möchten wahrscheinlich erwähnen, dass Sie dann wieder beitreten müssen.
Tomdemuyt
Ein ähnlicher Ansatz wird verwendet replace str.replace(/re/g, ''), dann besteht keine Notwendigkeit, sie erneut zu verbinden. auch wenn du ein schönes schleppendes einwirfst? wie str.replace(/\re\s?/g, '')dann werden Sie alle doppelten Leerzeichen los, die Sie von etwas haben würden, das in der Mitte einer Zeichenfolge ersetzt wird
jakecraige
0

Meine Antwort hier könnte auch Ihr Problem lösen:

https://stackoverflow.com/a/27967674/543814

  • Anstelle von Ersetzen würden Sie Match verwenden.
  • Anstelle von Gruppe $1würden Sie Gruppe lesen $2.
  • Die Gruppe $2wurde dort nicht erfasst, was Sie vermeiden würden.

Beispiel:

Regex.Match("50% of 50% is 25%", "(\d+\%)|(.+?)");

Die erste Erfassungsgruppe gibt das Muster an, das Sie vermeiden möchten. Die letzte Erfassungsgruppe erfasst alles andere. Lesen Sie einfach diese Gruppe vor $2.

Timo
quelle
0
(B)|(A)

Verwenden Sie dann, was Gruppe 2 erfasst ...

DW.
quelle
Er muss nicht B erfassen , er möchte nicht einfach alle B-Muster ignorieren.
Hexicle