Espressioni regolari: esiste un operatore AND?


708

Ovviamente, puoi usare |(pipe?) Per rappresentare OR, ma c'è anche un modo per rappresentare AND?

In particolare, vorrei abbinare i paragrafi di testo che contengono TUTTO di una determinata frase, ma in nessun ordine particolare.


1
Vuoi dire che vuoi trovare frasi in un testo, dove ciascuna di queste frasi è una permutazione valida delle parole in una determinata frase?
Nietzche-jou,

2
Lo sto mettendo qui perché tre o quattro risposte lo ignorano. Lookahead non corrisponde alla stessa lunghezza per ogni clausola, a meno che non finiscano in $. Un lookahead potrebbe corrispondere a quattro caratteri e un altro 6. Ad esempio, (? = A *) (? = Aab) corrisponderà a aabaaaaba
Zachary Vance,

2
prova a usare solo il carattere "spazio" per l'operatore "AND".

1 I'd like to match paragraphs of text. 2. Contiene testo fuori ordine . Il numero 1 è aperto all'interpretazione. Il numero 2 può essere fatto in due modi. Way 1:, (?:(?:(?(1)(?!))\b(phrase1)\b.*?|(?(2)(?!))\b(phrase2)\b.*?)){2}Way 2: (?=.*\bphrase1\b)(?=.*\bphrase2\b)dove in questo caso la corrispondenza del paragrafo in questo caso non è definita fino a quando la definizione di paragrafo non è formalizzata.

Risposte:


385

Utilizzare un'espressione regolare senza consumo.

La notazione tipica (cioè Perl / Java) è:

(?=espr)

Questo significa "match expr, ma dopo continua la corrispondenza nel punto di incontro originale".

Puoi fare quanti di questi vuoi e questo sarà un "e". Esempio:

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

È anche possibile aggiungere gruppi di acquisizione all'interno delle espressioni non consumate se è necessario salvare alcuni dei dati in essa contenuti.


3
perl -e "q {alcune cose e cose} = ~ / (? = some) (? = stuff) (? = things) /? print 'yes': stampa 'no'" stampa 'no'.
Robert P,

27
Va detto che questo particolare esempio è chiamato un'affermazione lookahead positiva. Ha altri usi oltre a "e". Si noti che il testo non viene utilizzato.
Strager

7
Usando (? =) In questo modo si ottiene una regex che non potrà mai avere successo. Ma è la congiunzione analoga a |. L'OP è semplicemente sbagliato in ciò che pensa risolverà il suo problema.
Nietzche-jou,

10
perl -e "q {alcune cose e cose} = ~ /(?=.*some)(?=.*stuff)(?=.*things)/? print 'yes': print 'no'"
kriss

3
Puoi per favore aggiungere qualche semplice esempio nel codice perl nella tua risposta?
Pithikos,

343

Devi usare il lookahead come hanno detto alcuni degli altri responder, ma il lookahead deve tenere conto di altri caratteri tra la sua parola target e la posizione attuale della corrispondenza. Per esempio:

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

Il .*primo lookahead lo fa corrispondere al numero di caratteri necessario prima che arrivi a "word1". Quindi la posizione della partita viene ripristinata e il secondo lookahead cerca "word2". Ripristina nuovamente e la parte finale corrisponde a "word3"; poiché è l'ultima parola che stai cercando, non è necessario che sia in uno sguardo, ma non fa male.

Per abbinare un intero paragrafo, devi ancorare la regex su entrambe le estremità e aggiungere un finale .*per consumare i personaggi rimanenti. Usando la notazione in stile Perl, sarebbe:

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

Il modificatore 'm' è per la modalità multilinea; lascia che ^e $corrisponda ai limiti di paragrafo ("limiti di linea" in regex-speak). È essenziale in questo caso che non si usi il modificatore 's', che consente al metacarattere punto di abbinare le nuove linee così come tutti gli altri caratteri.

Infine, vuoi assicurarti di abbinare parole intere e non solo frammenti di parole più lunghe, quindi devi aggiungere limiti di parole:

/^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$/m

8
Esatto, c'è anche un tutorial su questo! ocpsoft.org/tutorials/regular-expressions/and-in-regex
Lincoln

9
Grazie mille. * Questo fa la differenza
Gennadiy Ryabkin,

1
+1 per una risposta chiara e concisa che mostra uno dei migliori utilizzi per i lookahead (a differenza di usi come un hack per contare la corrispondenza percentuale di una password). :)
zx81,

1
@Liam :. MySQL utilizza il sapore POSIX ERE, quindi no. Sacrifica efficacemente le caratteristiche a favore delle prestazioni, il che mi sembra ragionevole. Ci sono maggiori informazioni qui .
Alan Moore,

3
sostituisci .*con [\s\S]*in javascript se hai nuove righe come .nel motore regex di javascript non corrisponde a nuove righe e non è possibile modificarle con i modificatori
Wesley Smith

41

Guarda questo esempio:

Abbiamo 2 regexps A e B e vogliamo abbinarli entrambi, quindi nello pseudo-codice è simile al seguente:

pattern = "/A AND B/"

Può essere scritto senza usare l'operatore AND in questo modo:

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

in PCRE:

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

regexp_match(pattern,data)

24
Questo è vero in termini di logica formale, ma qui non è assolutamente d'aiuto. Nelle regex, NOT può essere ancora più difficile da esprimere di AND.
Alan Moore,

@marvin_dpr Ha funzionato per me in CMake mentre l'altro suggerimento (?=expr)no. Sembra dipendere dall'implementazione.
Melebio

38
Non ^significa "inizio della stringa" nella sintassi regex?
Lambda Fairy,

3
In regex in generale, la ^negazione è solo all'inizio di una classe di caratteri. A meno che CMake non stia facendo qualcosa di veramente strano (al punto in cui chiamare il loro linguaggio di corrispondenza dei pattern "regex" potrebbe essere considerato fuorviante o errato), immagino che il fatto che abbia funzionato per te sia stato un incidente isolato.
tripla il

29

Puoi farlo con un'espressione regolare ma probabilmente vorrai qualcun altro. Ad esempio, usa diversi regexp e combinali in una clausola if.

Puoi enumerare tutte le possibili permutazioni con una regexp standard, in questo modo (corrisponde a, bec in qualsiasi ordine):

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

Tuttavia, questo rende una regexp molto lunga e probabilmente inefficiente, se hai più di un paio di termini.

Se stai usando una versione regexp estesa, come Perl o Java, hanno modi migliori per farlo. Altre risposte hanno suggerito di utilizzare un'operazione lookahead positiva.


10
Non credo che il tuo approccio sia più inefficiente di 3 lookaheads con il loro catastrofico backtracking. Certo, è più lungo da scrivere, ma tieni presente che puoi facilmente generare automaticamente il modello. Nota che puoi migliorarlo per fallire più velocemente con a(bc|cb)|b(ac|ca)|c(ab|ba). E il più importante, puoi usarlo con tutto il sapore regex.
Casimir et Hippolyte,

27

L'operatore AND è implicito nella sintassi RegExp.
L'operatore OR deve invece essere specificato con una pipe.
Il seguente RegExp:

var re = /ab/;

significa la lettera a E la lettera b.
Funziona anche con gruppi:

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

significa il gruppo co E il gruppo de.
Sostituire AND (implicito) con un OR richiederebbe le seguenti righe:

var re = /a|b/;
var re = /(co)|(de)/;

29
Sfortunatamente, questo non è ciò che l'OP ha richiesto. Questo trova qualsiasi cosa in quell'ordine, mentre loro li volevano in qualsiasi ordine. Controlla la risposta di stackoverflow.com/users/20938/alan-moore di seguito, che è quella corretta.
GESii

1
@JESii grazie per il tuo punto, hai ragione e ho frainteso la domanda di Hugoware, mi sono concentrato in particolare sulla sua prima frase. La risposta giusta è un uso corretto dell'operatore lookahead, come ha scritto AlanMoore. Ad ogni modo, penso che qualcuno possa trovare utile il mio chiarimento, dato che è già stato votato, quindi non butterei via tutto. Saluti.
Emanuele Del Grande,

13

Nel tuo caso non è possibile eseguire l'AND su più risultati corrispondenti? in pseudocodice

regexp_match(pattern1, data) && regexp_match(pattern2, data) && ...

3
Sono in una situazione in cui ho del codice che è una tabella di dati delle regole, con una singola stringa di corrispondenza del modello regex per testare la validità della regola. Passare a più test non è qualcosa che posso fare nel mio caso, e comunemente anche in altri casi!
Alan Wolfe,

11

Perché non usare Awk?
con awk regex AND, OR materia è così semplice

awk '/WORD1/ && /WORD2/ && /WORD3/' myfile

9

Se usi espressioni regolari Perl, puoi usare lookahead positivo:

Per esempio

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

sarebbero numeri maggiori di 100 e divisibili per 5


8

È possibile reindirizzare l'output a un altro regex. Usando grep, potresti fare questo:

grep A | grep B


8

Oltre alla risposta accettata

Ti fornirò alcuni esempi pratici che renderanno le cose più chiare per alcuni di voi. Ad esempio, supponiamo che abbiamo queste tre righe di testo:

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

Guarda la demo qui DEMO

Quello che vogliamo fare qui è selezionare il segno + ma solo se è dopo due numeri con uno spazio e se è prima di quattro numeri. Questi sono gli unici vincoli. Useremmo questa espressione regolare per raggiungerla:

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

Nota se si separa l'espressione ti darà risultati diversi.

O forse vuoi selezionare del testo tra i tag ... ma non i tag! Quindi puoi usare:

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

per questo testo:

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

Guarda la demo qui DEMO


Quale risposta è stata la risposta accettata? Si prega di aggiungere un link ad esso per il futuro me.
James Brown,

6

L'ordine è sempre implicito nella struttura dell'espressione regolare. Per ottenere ciò che desideri, dovrai abbinare più volte la stringa di input a espressioni diverse.

Quello che vuoi fare non è possibile con una sola regexp.


Non è tecnicamente impossibile, ma non vale la pena implementarlo. Non so perché qualcuno abbia effettuato il downvoting ...
Robert P,

13
Probabilmente perché non è solo possibile, è semplice, supponendo che il tuo sapore regex supporti lookaheads. E questa è una buona scommessa; la maggior parte dei principali linguaggi di programmazione odierni li supporta.
Alan Moore,

3

Usa AND al di fuori dell'espressione regolare. In PHP l'operatore lookahead non sembrava funzionare per me, invece l'ho usato

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

Il regex precedente corrisponderà se la lunghezza della password è di 3 caratteri o più e non ci sono spazi nella password.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.