Wyrażenia regularne: czy istnieje operator AND?

707

Oczywiście możesz użyć |(potoku?) Do reprezentowania OR, ale czy istnieje sposób na reprezentację AND?

W szczególności chciałbym dopasować akapity tekstu, które zawierają WSZYSTKIE określonej frazy, ale nie w określonej kolejności.

Hugoware
źródło
1
Czy masz na myśli, że chcesz znaleźć frazy w tekście, gdzie każda taka fraza jest prawidłową kombinacją słów w danej frazie?
Nietzche-jou
2
Kładę to tutaj, ponieważ trzy lub cztery odpowiedzi ignorują to. Lookahead nie pasuje do tej samej długości dla każdej klauzuli, chyba że kończy się na $. Jeden lookahead może pasować do czterech znaków, a drugi 6. Na przykład (? = A *) (? = Aab) będzie pasować aabaaaaba
Zachary Vance
2
spróbuj użyć tylko „spacji” dla operatora „AND”.
1 I'd like to match paragraphs of text.. 2. Zawierające out-of-order tekst. Numer 1 jest otwarty na interpretację. Numer 2 można zrobić na kilka sposobów. Sposób 1 (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}:, Sposób 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)w tym przypadku dopasowanie akapitu w tym przypadku jest niezdefiniowane do momentu sformalizowania definicji akapitu.

Odpowiedzi:

385

Użyj niepotrzebnego wyrażenia regularnego.

Typowa notacja (tj. Perl / Java) to:

(?=expr)

Oznacza to „dopasuj wyrażenie, ale potem kontynuuj dopasowanie w pierwotnym punkcie dopasowania”.

Możesz zrobić tyle, ile chcesz, a to będzie „i”. Przykład:

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

Możesz nawet dodać grupy przechwytywania do niepotrzebnych wyrażeń, jeśli chcesz zapisać w nich niektóre dane.

Jason Cohen
źródło
3
perl -e "q {niektóre rzeczy i rzeczy} = ~ / (? = niektóre) (? = rzeczy) (? = rzeczy) /? drukuj„ tak ”: drukuj„ nie ”„ drukuje ”nie”.
Robert P
27
Należy wspomnieć, że ten konkretny przykład nazywa się twierdzeniem o pozytywnym spojrzeniu w przyszłość. Ma inne zastosowania niż „i”. Pamiętaj, że tekst nie jest zużywany.
strager
7
Użycie (? =) W ten sposób powoduje wyrażenie regularne, które nigdy się nie powiedzie. Ale jest to koniunkcja analogiczna do | OP jest po prostu zły w tym, co według niego rozwiąże jego problem.
Nietzche-jou
10
perl -e "q {niektóre rzeczy i rzeczy} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print 'yes': print 'no'”
kriss
3
Czy możesz w swojej odpowiedzi podać prosty przykład w kodzie Perl?
Pithikos,
343

Musisz użyć lookahead, jak powiedzieli niektórzy inni respondenci, ale lookahead musi uwzględnić inne znaki między słowem docelowym a bieżącą pozycją dopasowania. Na przykład:

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

.*W pierwszym uprzedzona Pozwala dopasować jednak wiele znaków potrzebnych do zanim dojdzie do „wskazywanym przez zmienną WORD1”. Następnie pozycja dopasowania jest resetowana, a drugi lookahead szuka „word2”. Zresetuj ponownie, a końcowa część pasuje do „słowa 3”; ponieważ jest to ostatnie słowo, które sprawdzasz, nie jest konieczne, aby było to spojrzenie w przyszłość, ale nie boli.

Aby dopasować cały akapit, musisz zakotwiczyć regex na obu końcach i dodać finał, .*aby pochłonąć pozostałe postacie. Używając notacji w stylu Perla, byłoby to:

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

Modyfikator „m” dotyczy trybu wieloliniowego; pozwala ^i $dopasowywać na granicach akapitów („granic linii” w regex-speak). W tym przypadku istotne jest, aby nie używać modyfikatora „s”, który pozwala metaznakowi kropkowemu dopasowywać znaki nowej linii, a także wszystkie inne znaki.

Na koniec chcesz się upewnić, że dopasowujesz całe słowa, a nie tylko fragmenty dłuższych słów, więc musisz dodać granice słów:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m
Alan Moore
źródło
8
Dokładnie tak - jest też samouczek na ten temat! ocpsoft.org/tutorials/regular-expressions/and-in-regex
Lincoln
9
Wielkie dzięki. * To robi różnicę
Gennadiy Ryabkin
1
+1 za jasną i szybką odpowiedź pokazującą jedno z najlepszych zastosowań dla lookaheads (w przeciwieństwie do zastosowań takich jak hack, aby policzyć procentowe dopasowanie hasła). :)
zx81
1
@Liam :. MySQL używa smaku POSIX ERE, więc nie. Skutecznie poświęca funkcje na rzecz wydajności, co wydaje mi się rozsądne. Jest więcej informacji tutaj .
Alan Moore,
3
wymienić .*z [\s\S]*javascript, jeśli masz nowe linie jak .w JavaScript za regex silnika nie zgadza się nowe linie i nie mogą być wykonane z modyfikatorów
Wesley Smith
41

Spójrz na ten przykład:

Mamy 2 wyrażenia regularne A i B i chcemy dopasować oba, więc w pseudokodzie wygląda to tak:

pattern = "/A AND B/"

Można go napisać bez użycia operatora AND w następujący sposób:

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

w PCRE:

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

regexp_match(pattern,data)
fanjabi
źródło
24
To prawda, jeśli chodzi o logikę formalną, ale tutaj absolutnie nic nie pomoże. W wyrażeniach regularnych NIE może być nawet trudniej wyrazić niż AND.
Alan Moore
@marvin_dpr To działało dla mnie w CMake, podczas gdy inne sugestie (?=expr)nie. Wydaje się, że zależy od implementacji.
Melebius
38
Nie ^oznacza „początku łańcucha” w składni wyrażenia regularnego?
Lambda Fairy
3
W wyrażeniu regularnym w ogóle ^jest negacją tylko na początku klasy postaci. Chyba że CMake robi coś naprawdę fajnego (do tego stopnia, że ​​nazwanie ich języka „regex” dopasowującym wzorce może być uznane za mylące lub niepoprawne), zgaduję, że zadziałało to dla ciebie na białym tle.
tripleee
29

Możesz to zrobić za pomocą wyrażenia regularnego, ale prawdopodobnie będziesz chciał zrobić coś innego. Na przykład użyj kilku wyrażeń regularnych i połącz je w klauzuli if.

Możesz wyliczyć wszystkie możliwe kombinacje za pomocą standardowego wyrażenia regularnego, takiego jak to (dopasowuje a, b i c w dowolnej kolejności):

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

Jednak powoduje to bardzo długi i prawdopodobnie nieefektywny regexp, jeśli masz więcej niż kilka terminów.

Jeśli używasz rozszerzonej wersji wyrażenia regularnego, takiej jak Perl lub Java, mają lepsze sposoby na to. Inne odpowiedzi sugerują użycie pozytywnego działania wyprzedzającego.

Juha Syrjälä
źródło
10
Nie sądzę, aby twoje podejście było bardziej nieefektywne niż 3 spojrzenia w przód z ich katastrofalnym wycofywaniem. Oczywiście, pisanie jest dłuższe, ale pamiętaj, że możesz łatwo wygenerować wzór automatycznie. Pamiętaj, że możesz go poprawić, by szybciej działał a(bc|cb)|b(ac|ca)|c(ab|ba). A co najważniejsze, możesz go używać ze wszystkimi smakami wyrażeń regularnych.
Casimir et Hippolyte
26

Operator AND jest niejawny w składni RegExp.
Zamiast tego operator OR należy podać za pomocą potoku.
Następujący RegExp:

var re = /ab/;

oznacza literę a I literę b.
Działa również z grupami:

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

oznacza grupę co ORAZ grupę de.
Zastąpienie (niejawne) AND operatorem OR wymagałoby następujących wierszy:

var re = /a|b/;
var re = /(co)|(de)/;
Emanuele Del Grande
źródło
29
Niestety nie o to prosił PO. Znajduje to wszystko w tej kolejności, podczas gdy oni chcieli ich w dowolnej kolejności. Sprawdź odpowiedź na stackoverflow.com/users/20938/alan-moore poniżej, która jest poprawna.
JESii
1
@JESii dziękuję za twoją uwagę, masz rację i źle zrozumiałem pytanie Hugoware'a, skupiłem się szczególnie na jego pierwszym zdaniu. Prawidłowa odpowiedź to właściwe użycie operatora lookahead, jak napisał AlanMoore. W każdym razie myślę, że ktoś może uznać moje wyjaśnienie za przydatne, ponieważ zostało to już ocenione, więc nie wyrzucę wszystkiego. Pozdrowienia.
Emanuele Del Grande,
13

Czy w twoim przypadku nie jest możliwe wykonanie AND dla kilku pasujących wyników? w pseudokodzie

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...
użytkownik54579
źródło
3
Mam sytuację, w której mam kod, który jest tabelą danych reguł, z pojedynczym ciągiem dopasowania wzorca regularnego, aby przetestować poprawność reguły. Przejście do wielu testów nie jest czymś, co mogę zrobić w moim przypadku, a często także w przypadku innych ludzi!
Alan Wolfe,
11

Dlaczego nie skorzystać z awk?
z awk regex AND, OR sprawy są takie proste

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile
mug896
źródło
9

Jeśli używasz wyrażeń regularnych Perla, możesz użyć pozytywnego wyglądu:

Na przykład

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

byłyby liczbami większymi niż 100 i podzielnymi przez 5

jpalecek
źródło
8

Możesz przesłać dane wyjściowe do innego wyrażenia regularnego. Używając grep, możesz to zrobić:

grep A | grep B

Śmieciarz
źródło
8

Oprócz zaakceptowanej odpowiedzi

Dam ci kilka praktycznych przykładów, które sprawią, że niektórzy z was staną się bardziej klarowni. Powiedzmy na przykład, że mamy te trzy wiersze tekstu:

[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]

Zobacz demo tutaj DEMO

Chcemy tutaj wybrać znak +, ale tylko wtedy, gdy występuje po dwóch liczbach ze spacją i jeśli występuje przed czterema liczbami. To są jedyne ograniczenia. Aby to osiągnąć, użylibyśmy tego wyrażenia regularnego:

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

Zauważ, że jeśli oddzielisz wyrażenie, da to różne wyniki.

A może chcesz zaznaczyć tekst między znacznikami ... ale nie znaczniki! Następnie możesz użyć:

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

dla tego tekstu:

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

Zobacz demo tutaj DEMO

DevWL
źródło
Która odpowiedź była odpowiedzią zaakceptowaną? Dodaj link do niego dla mnie w przyszłości.
James Brown,
6

Kolejność jest zawsze implikowana w strukturze wyrażenia regularnego. Aby osiągnąć to, co chcesz, musisz wielokrotnie dopasować ciąg wejściowy do różnych wyrażeń.

To, co chcesz zrobić, nie jest możliwe za pomocą jednego wyrażenia regularnego.

pilif
źródło
Nie jest to technicznie niemożliwe, ale nie jest warte wdrożenia. Nie wiem, dlaczego ktoś przegłosował ...
Robert P.
13
Prawdopodobnie dlatego, że jest to nie tylko możliwe, ale proste, zakładając, że Twój smak regularny obsługuje oczekiwania. I to jest dobry zakład; obsługuje większość współczesnych głównych języków programowania.
Alan Moore
3

Użyj AND poza wyrażeniem regularnym. W PHP operator lookahead nie działał dla mnie, zamiast tego użyłem tego

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

Powyższy regex będzie pasował, jeśli długość hasła wynosi 3 znaki lub więcej i hasło nie zawiera spacji.

Hammad Khan
źródło