Reguläre Ausdrücke: Gibt es einen UND-Operator?

708

Natürlich können Sie die |(Pipe?) Zum Darstellen verwenden OR, aber gibt es auch eine Möglichkeit zum Darstellen AND?

Insbesondere möchte ich Textabschnitte abgleichen, die ALLE einer bestimmten Phrase enthalten, jedoch in keiner bestimmten Reihenfolge.

Hugoware
quelle
1
Meinen Sie damit, dass Sie Phrasen in einem Text finden möchten, wobei jede solche Phrase eine gültige Permutation der Wörter in einer bestimmten Phrase ist?
Nietzche-jou
2
Ich stelle das hier auf, weil drei oder vier Antworten es ignorieren. Lookahead entspricht nicht für jede Klausel der gleichen Länge, es sei denn, sie enden mit $. Ein Lookahead könnte mit vier Zeichen und ein weiterer mit 6 übereinstimmen. Zum Beispiel wird (? = A *) (? = Aab) mit aabaaaaba
Zachary Vance
2
Versuchen Sie, nur das Leerzeichen für den Operator "AND" zu verwenden.
1 I'd like to match paragraphs of text.. 2. Enthält nicht ordnungsgemäßen Text. Nummer 1 ist offen für Interpretationen. Nummer 2 kann auf verschiedene Arten erfolgen. Weg 1 : (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}, Weg 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)In diesem Fall ist die Übereinstimmung des Absatzes in diesem Fall undefiniert, bis die Definition des Absatzes formalisiert ist.

Antworten:

385

Verwenden Sie einen nicht konsumierenden regulären Ausdruck.

Die typische Notation (dh Perl / Java) lautet:

(?=Ausdruck)

Dies bedeutet "Match- Ausdruck, aber danach wird der Matching am ursprünglichen Match-Punkt fortgesetzt."

Sie können so viele davon machen, wie Sie möchten, und dies wird ein "und" sein. Beispiel:

(?=match this expression)(?=match this too)(?=oh, and this)

Sie können sogar Erfassungsgruppen zu den nicht konsumierenden Ausdrücken hinzufügen, wenn Sie einige der darin enthaltenen Daten speichern müssen.

Jason Cohen
quelle
3
perl -e "q {einige Sachen und Dinge} = ~ / (? = einige) (? = Sachen) (? = Dinge) /? print 'yes': print 'no'" druckt 'no'.
Robert P
27
Es sollte erwähnt werden, dass dieses spezielle Beispiel als positive Lookahead-Behauptung bezeichnet wird. Es hat andere Verwendungen als "und". Beachten Sie, dass der Text nicht verwendet wird.
Strager
7
Die Verwendung von (? =) So führt zu einem regulären Ausdruck, der niemals erfolgreich sein kann. Aber es ist die Konjunktion analog zu |. Das OP ist einfach falsch in dem, was er glaubt, um sein Problem zu lösen.
Nietzche-jou
10
perl -e "q {einige Sachen und Dinge} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print 'yes': print 'no'"
kriss
3
Können Sie Ihrer Antwort bitte ein einfaches Beispiel im Perl-Code hinzufügen?
Pithikos
343

Sie müssen Lookahead verwenden, wie einige der anderen Antwortenden gesagt haben, aber der Lookahead muss andere Zeichen zwischen seinem Zielwort und der aktuellen Übereinstimmungsposition berücksichtigen. Zum Beispiel:

(?=.*word1)(?=.*word2)(?=.*word3)

Mit .*dem ersten Lookahead können Sie die Anzahl der Zeichen abgleichen, die erforderlich sind, bevor "word1" angezeigt wird. Dann wird die Übereinstimmungsposition zurückgesetzt und der zweite Lookahead sucht nach "word2". Erneut zurücksetzen und der letzte Teil stimmt mit "word3" überein; Da es das letzte Wort ist, nach dem Sie suchen, ist es nicht notwendig, dass es sich um einen Lookahead handelt, aber es tut nicht weh.

Um einem ganzen Absatz zu entsprechen, müssen Sie den regulären Ausdruck an beiden Enden verankern und ein Finale hinzufügen .*, um die verbleibenden Zeichen zu verbrauchen. Bei Verwendung der Perl-Notation wäre dies:

/^(?=.*word1)(?=.*word2)(?=.*word3).*$/m

Der Modifikator 'm' ist für den Mehrzeilenmodus vorgesehen. es lässt das ^und $an Absatzgrenzen übereinstimmen ("Zeilengrenzen" in Regex-Sprache). In diesem Fall ist es wichtig, dass Sie nicht den Modifikator 's' verwenden, mit dem das Punkt-Metazeichen sowohl mit Zeilenumbrüchen als auch mit allen anderen Zeichen übereinstimmt.

Schließlich möchten Sie sicherstellen, dass Sie ganze Wörter und nicht nur Fragmente längerer Wörter abgleichen. Daher müssen Sie Wortgrenzen hinzufügen:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
Alan Moore
quelle
8
Genau richtig - dazu gibt es auch ein Tutorial! ocpsoft.org/tutorials/regular-expressions/and-in-regex
Lincoln
9
Vielen Dank. * Das macht einen Unterschied
Gennadiy Ryabkin
1
+1 für eine klare und prägnante Antwort, die eine der besten Verwendungsmöglichkeiten für Lookaheads darstellt (im Gegensatz zu Verwendungen wie einem Hack zum Zählen der prozentualen Übereinstimmung eines Kennworts). :)
zx81
1
@ Liam:. MySQL verwendet die POSIX ERE-Variante, also nein. Es opfert effektiv Funktionen zugunsten der Leistung, was mir vernünftig erscheint. Es stehen weitere Informationen hier .
Alan Moore
3
Ersetzen Sie .*durch [\s\S]*in Javascript, wenn Sie neue Zeilen haben, da .die Regex-Engine von Javascript nicht mit neuen Zeilen übereinstimmt und nicht mit Modifikatoren bearbeitet werden kann
Wesley Smith
41

Schauen Sie sich dieses Beispiel an:

Wir haben 2 reguläre Ausdrücke A und B und wir möchten beide übereinstimmen, also sieht es im Pseudocode so aus:

pattern = "/A AND B/"

Es kann ohne Verwendung des AND-Operators wie folgt geschrieben werden:

pattern = "/NOT (NOT A OR NOT B)/"

in PCRE:

"/(^(^A|^B))/"

regexp_match(pattern,data)
Fanjabi
quelle
24
Das stimmt in Bezug auf die formale Logik, aber es ist hier absolut keine Hilfe. In regulären Ausdrücken kann es noch schwieriger sein, NOT auszudrücken als AND.
Alan Moore
@marvin_dpr Es hat bei mir in CMake funktioniert, der andere Vorschlag (?=expr)nicht. Es scheint implementierungsabhängig zu sein.
Melebius
38
Bedeutet nicht ^"Anfang der Zeichenfolge" in der Regex-Syntax?
Lambda Fairy
3
In Regex ^ist Negation im Allgemeinen nur am Anfang einer Zeichenklasse. Es sei denn, CMake macht etwas wirklich Funky (bis zu dem Punkt, an dem die Bezeichnung der Mustervergleichssprache "Regex" als irreführend oder falsch angesehen werden könnte), war die Tatsache, dass es für Sie funktioniert hat, ein Einzelfall.
Tripleee
29

Sie können dies mit einem regulären Ausdruck tun, aber wahrscheinlich möchten Sie etwas anderes. Verwenden Sie beispielsweise mehrere reguläre Ausdrücke und kombinieren Sie sie in einer if-Klausel.

Sie können alle möglichen Permutationen mit einem Standard-Regexp wie folgt auflisten (entspricht a, b und c in beliebiger Reihenfolge):

(abc)|(bca)|(acb)|(bac)|(cab)|(cba)

Dies führt jedoch zu einem sehr langen und wahrscheinlich ineffizienten regulären Ausdruck, wenn Sie mehr als ein paar Begriffe haben.

Wenn Sie eine erweiterte Regexp-Version wie Perl oder Java verwenden, haben sie bessere Möglichkeiten, dies zu tun. Andere Antworten haben vorgeschlagen, eine positive Lookahead-Operation zu verwenden.

Juha Syrjälä
quelle
10
Ich denke nicht, dass Ihr Ansatz ineffizienter ist als 3 Lookaheads mit ihrem katastrophalen Backtracking. Sicher, das Schreiben dauert länger, aber beachten Sie, dass Sie das Muster problemlos automatisch generieren können. Beachten Sie, dass Sie es verbessern können, um schneller zu scheitern a(bc|cb)|b(ac|ca)|c(ab|ba). Und das Wichtigste ist, dass Sie es mit allen Regex-Aromen verwenden können.
Casimir et Hippolyte
27

Der AND - Operator ist implizit in der RegExp - Syntax.
Der OR-Operator muss stattdessen mit einer Pipe angegeben werden.
Das folgende RegExp:

var re = /ab/;

bedeutet den Buchstaben a UND den Buchstaben b.
Es funktioniert auch mit Gruppen:

var re = /(co)(de)/;

es bedeutet die Gruppe co UND die Gruppe de.
Das Ersetzen des (impliziten) UND durch ein ODER würde die folgenden Zeilen erfordern:

var re = /a|b/;
var re = /(co)|(de)/;
Emanuele Del Grande
quelle
29
Leider hat das OP nicht darum gebeten. Dies findet alles in dieser Reihenfolge, während sie sie in beliebiger Reihenfolge wollten. Schauen Sie sich die Antwort von stackoverflow.com/users/20938/alan-moore an, unter der die richtige steht.
JESii
1
@JESii danke für deinen Punkt, du hast recht und ich habe die Frage von Hugoware falsch verstanden, ich habe mich besonders auf seinen ersten Satz konzentriert. Die richtige Antwort ist die richtige Verwendung des Lookahead-Operators, wie AlanMoore schrieb. Jedenfalls denke ich, dass jemand meine Klarstellung nützlich finden könnte, da sie bereits positiv bewertet wurde, sodass ich nicht alles wegwerfen würde. Grüße.
Emanuele Del Grande
13

Ist es in Ihrem Fall nicht möglich, das UND für mehrere übereinstimmende Ergebnisse durchzuführen? im Pseudocode

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...
user54579
quelle
3
Ich bin in einer Situation, in der ich Code habe, der eine Datentabelle von Regeln ist, mit einer einzelnen Regex-Muster-Übereinstimmungszeichenfolge, um die Gültigkeit der Regel zu testen. Die Umstellung auf mehrere Tests kann ich in meinem Fall nicht tun, und normalerweise auch in anderen Fällen!
Alan Wolfe
11

Warum nicht awk verwenden?
mit awk regex AND, OR ist so einfach

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile
mug896
quelle
9

Wenn Sie reguläre Perl-Ausdrücke verwenden, können Sie einen positiven Lookahead verwenden:

Zum Beispiel

(?=[1-9][0-9]{2})[0-9]*[05]\b

wäre Zahlen größer als 100 und teilbar durch 5

jpalecek
quelle
8

Sie können Ihre Ausgabe an einen anderen regulären Ausdruck weiterleiten. Mit grep können Sie Folgendes tun:

grep A | grep B

Müllsammler
quelle
8

Neben der akzeptierten Antwort

Ich werde Ihnen einige praktische Beispiele geben, die einigen von Ihnen die Dinge klarer machen. Nehmen wir zum Beispiel an, wir haben diese drei Textzeilen:

[12/Oct/2015:00:37:29 +0200] // only this + will get selected
[12/Oct/2015:00:37:x9 +0200]
[12/Oct/2015:00:37:29 +020x]

Siehe Demo hier DEMO

Was wir hier tun möchten, ist das + -Zeichen auszuwählen, aber nur, wenn es nach zwei Zahlen mit einem Leerzeichen und vor vier Zahlen steht. Das sind die einzigen Einschränkungen. Wir würden diesen regulären Ausdruck verwenden, um dies zu erreichen:

'~(?<=\d{2} )\+(?=\d{4})~g'

Wenn Sie den Ausdruck trennen, erhalten Sie unterschiedliche Ergebnisse.

Oder vielleicht möchten Sie Text zwischen Tags auswählen ... aber nicht die Tags! Dann könnten Sie verwenden:

'~(?<=<p>).*?(?=<\/p>)~g'

für diesen Text:

<p>Hello !</p> <p>I wont select tags! Only text with in</p> 

Siehe Demo hier DEMO

DevWL
quelle
Welche Antwort wurde akzeptiert? Bitte fügen Sie mir einen Link für die Zukunft hinzu.
James Brown
6

Die Reihenfolge ist immer in der Struktur des regulären Ausdrucks enthalten. Um das zu erreichen, was Sie wollen, müssen Sie die Eingabezeichenfolge mehrmals mit verschiedenen Ausdrücken abgleichen.

Was Sie tun möchten, ist mit einem einzigen regulären Ausdruck nicht möglich.

Pilif
quelle
Es ist technisch nicht unmöglich, aber die Implementierung lohnt sich nicht. Ich weiß nicht, warum jemand herabgestimmt hat ...
Robert P
13
Wahrscheinlich, weil es nicht nur möglich, sondern auch einfach ist, vorausgesetzt, Ihr Regex-Geschmack unterstützt Lookaheads. Und das ist eine gute Wette; Die meisten der wichtigsten Programmiersprachen von heute unterstützen sie.
Alan Moore
3

Verwenden Sie AND außerhalb des regulären Ausdrucks. In PHP schien der Lookahead-Operator für mich nicht zu funktionieren, stattdessen habe ich ihn verwendet

if( preg_match("/^.{3,}$/",$pass1) && !preg_match("/\s{1}/",$pass1))
    return true;
else
    return false;

Der obige reguläre Ausdruck stimmt überein, wenn die Kennwortlänge 3 Zeichen oder mehr beträgt und das Kennwort keine Leerzeichen enthält.

Hammad Khan
quelle