Espressione regolare per trovare una riga che non contiene una parola
So che è possibile abbinare una parola e quindi invertire le corrispondenze utilizzando altri strumenti (ad esempio grep -v
). Tuttavia, è possibile abbinare le righe che non contengono una parola specifica, ad esempio hede
utilizzando un'espressione regolare?
Ingresso:
hoho
hihi
haha
hede
Codice:
grep "<Regex for 'doesn't contain hede'>" input
Uscita desiderata:
hoho
hihi
haha
Risposte
La nozione che regex non supporti la corrispondenza inversa non è del tutto vera. Puoi imitare questo comportamento utilizzando look-around negativi:
^((?!hede).)*$
L' espressione regolare sopra corrisponderà a qualsiasi stringa o riga senza interruzione di riga, non contenente la (sotto) stringa "hede". Come accennato, questo non è qualcosa in cui regex è "bravo" (o dovrebbe fare), ma comunque è possibile.
E se hai bisogno di abbinare anche i caratteri di interruzione di riga, usa il modificatore DOT-ALL (il finale s
nel seguente schema):
/^((?!hede).)*$/s
o usalo in linea:
/(?s)^((?!hede).)*$/
(dove /.../
sono i delimitatori regex, cioè non fanno parte del pattern)
Se il modificatore DOT-ALL non è disponibile, puoi imitare lo stesso comportamento con la classe del personaggio [\s\S]
:
/^((?!hede)[\s\S])*$/
Spiegazione
Una stringa è solo un elenco di n
caratteri. Prima e dopo ogni carattere, c'è una stringa vuota. Quindi un elenco di n
caratteri avrà n+1
stringhe vuote. Considera la stringa "ABhedeCD"
:
┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
└──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘
index 0 1 2 3 4 5 6 7
dove e
sono le stringhe vuote. L'espressione regolare (?!hede).
guarda avanti per vedere se non c'è sottostringa "hede"
da vedere, e se è così (quindi si vede qualcos'altro), il .
(punto) corrisponderà a qualsiasi carattere tranne un'interruzione di riga. I look-around sono anche chiamati asserzioni di larghezza zero perché non consumano alcun carattere. Affermano / convalidano solo qualcosa.
Quindi, nel mio esempio, ogni stringa vuota viene prima convalidata per vedere se non c'è "hede"
più avanti, prima che un carattere venga consumato dal .
(punto). La regex (?!hede).
farà che solo una volta, per cui è avvolto in un gruppo, e ripetuto zero o più volte: ((?!hede).)*
. Infine, l'inizio e la fine dell'input sono ancorati per assicurarsi che l'intero input venga consumato:^((?!hede).)*$
Come si può vedere, l'ingresso "ABhedeCD"
non riuscirà perché il e3
, l'espressione regolare (?!hede)
non riesce (non v'è "hede"
più avanti!).
Nota che la soluzione per non inizia con "hede" :
^(?!hede).*$
è generalmente molto più efficiente della soluzione per non contenere "hede" :
^((?!hede).)*$
Il primo verifica la presenza di "hede" solo nella prima posizione della stringa di input, piuttosto che in ogni posizione.
Se lo stai usando solo per grep, puoi usarlo grep -v hede
per ottenere tutte le righe che non contengono hede.
ETA Oh, rileggendo la domanda, grep -v
probabilmente è quello che intendevi per "opzioni degli strumenti".
Risposta:
^((?!hede).)*$
Spiegazione:
^
l'inizio della stringa, (
raggruppa e cattura a \ 1 (0 o più volte (corrispondente al maggior numero possibile)),
(?!
guarda avanti per vedere se non c'è,
hede
la tua corda,
)
fine del look-ahead, .
qualsiasi carattere eccetto \ n,
)*
fine di \ 1 (Nota: poiché stai utilizzando un quantificatore su questa cattura, solo l'ULTIMA ripetizione del pattern acquisito verrà memorizzata in \ 1)
$
prima di un \ n facoltativo, e la fine della stringa
Le risposte fornite vanno perfettamente bene, solo un punto accademico:
Le espressioni regolari nel significato delle scienze informatiche teoriche NON SONO IN GRADO di farlo in questo modo. Per loro doveva assomigliare a questo:
^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$)
Questo fa solo una corrispondenza COMPLETA. Farlo per le partite secondarie sarebbe anche più imbarazzante.
Se vuoi che il test regex fallisca solo se l' intera stringa corrisponde, funzionerà quanto segue:
^(?!hede$).*
es. - Se vuoi consentire tutti i valori eccetto "foo" (cioè "foofoo", "barfoo" e "foobar" passeranno, ma "foo" fallirà), usa: ^(?!foo$).*
Ovviamente, se stai verificando l' uguaglianza esatta , una soluzione generale migliore in questo caso è controllare l'uguaglianza delle stringhe, ad es
myStr !== 'foo'
Potresti anche mettere la negazione fuori dal test se hai bisogno di funzionalità regex (qui, insensibilità al maiuscolo / minuscolo e corrispondenza intervallo):
!/^[a-f]oo$/i.test(myStr)
La soluzione regex all'inizio di questa risposta può essere utile, tuttavia, in situazioni in cui è richiesto un test regex positivo (forse da un'API).
FWIW, poiché i linguaggi regolari (aka linguaggi razionali) sono chiusi sotto complementazione, è sempre possibile trovare un'espressione regolare (aka espressione razionale) che nega un'altra espressione. Ma non molti strumenti lo implementano.
Vcsn supporta questo operatore (che denota {c}
, suffisso).
Per prima cosa definisci il tipo delle tue espressioni: le etichette sono lettere ( lal_char
) da cui scegliere, a
ad z
esempio (definire l'alfabeto quando si lavora con la complementazione è, ovviamente, molto importante) e il "valore" calcolato per ogni parola è solo un booleano : true
la parola è accettata false
,, rifiutata.
In Python:
In [5]: import vcsn
c = vcsn.context('lal_char(a-z), b')
c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 𝔹
quindi inserisci la tua espressione:
In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c
converti questa espressione in un automa:
In [7]: a = e.automaton(); a
infine, riconvertire questo automa in un'espressione semplice.
In [8]: print(a.expression())
\e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*
dove di +
solito è indicato |
, \e
denota la parola vuota e di [^]
solito è scritto .
(qualsiasi carattere). Quindi, con un po 'di riscrittura ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*
.
Puoi vedere questo esempio qui e provare Vcsn online lì .
Ecco una buona spiegazione del motivo per cui non è facile negare un'espressione regolare arbitraria. Sono d'accordo con le altre risposte, però: se questa è qualcosa di diverso da una domanda ipotetica, allora una regex non è la scelta giusta qui.
Con lookahead negativo, l'espressione regolare può corrispondere a qualcosa che non contiene un pattern specifico. Questo viene risposto e spiegato da Bart Kiers. Ottima spiegazione!
Tuttavia, con la risposta di Bart Kiers, la parte di lookahead metterà alla prova da 1 a 4 caratteri in avanti mentre corrisponde a qualsiasi singolo carattere. Possiamo evitarlo e lasciare che la parte di lookahead controlli l'intero testo, assicurandoci che non ci sia "hede", e poi la parte normale (. *) Può mangiare l'intero testo tutto in una volta.
Ecco la regex migliorata:
/^(?!.*?hede).*$/
Nota che il quantificatore pigro (*?) Nella parte di lookahead negativo è opzionale, puoi invece usare (*) quantificatore avido, a seconda dei tuoi dati: se 'hede' è presente e nella metà iniziale del testo, il quantificatore pigro può essere più veloce; in caso contrario, il quantificatore avido sarà più veloce. Tuttavia, se "hede" non è presente, entrambi sarebbero ugualmente lenti.
Ecco il codice demo .
Per ulteriori informazioni su lookahead, consulta il fantastico articolo: Mastering Lookahead and Lookbehind .
Inoltre, controlla RegexGen.js , un generatore di espressioni regolari JavaScript che aiuta a costruire espressioni regolari complesse. Con RegexGen.js, puoi costruire la regex in un modo più leggibile:
var _ = regexGen;
var regex = _(
_.startOfLine(),
_.anything().notContains( // match anything that not contains:
_.anything().lazy(), 'hede' // zero or more chars that followed by 'hede',
// i.e., anything contains 'hede'
),
_.endOfLine()
);
Punti di riferimenti
Ho deciso di valutare alcune delle opzioni presentate e di confrontarne le prestazioni, nonché di utilizzare alcune nuove funzionalità. Benchmarking su .NET Regex Engine:http://regexhero.net/tester/
Testo benchmark:
Le prime 7 righe non dovrebbero corrispondere, poiché contengono l'espressione cercata, mentre le 7 righe inferiori dovrebbero corrispondere!
Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.
Risultati:
I risultati sono iterazioni al secondo come mediana di 3 esecuzioni: numero maggiore = migliore
01: ^((?!Regex Hero).)*$ 3.914 // Accepted Answer 02: ^(?:(?!Regex Hero).)*$ 5.034 // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$ 6.137 // Lookahead only on the right first letter 04: ^(?>(?:.*?Regex Hero)?)^.*$ 7.426 // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$ 7.371 // Logic Branch: Find Regex Hero? match nothing, else anything
P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT)) ????? // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ????? // Direct COMMIT & FAIL in Perl
Poiché .NET non supporta i verbi di azione (* FAIL, ecc.) Non ho potuto testare le soluzioni P1 e P2.
Sommario:
Ho provato a testare la maggior parte delle soluzioni proposte, alcune ottimizzazioni sono possibili per alcune parole. Ad esempio, se le prime due lettere della stringa di ricerca non sono uguali, la risposta 03 può essere espansa fino ^(?>[^R]+|R+(?!egex Hero))*$
a ottenere un piccolo aumento delle prestazioni.
Ma la soluzione complessiva più leggibile e più veloce dal punto di vista delle prestazioni sembra essere 05 utilizzando un'istruzione condizionale o 04 con il quantificatore possente. Penso che le soluzioni Perl dovrebbero essere ancora più veloci e più facilmente leggibili.
Non regex, ma ho trovato logico e utile usare greps seriali con pipe per eliminare il rumore.
per esempio. cerca un file di configurazione di apache senza tutti i commenti-
grep -v '\#' /opt/lampp/etc/httpd.conf # this gives all the non-comment lines
e
grep -v '\#' /opt/lampp/etc/httpd.conf | grep -i dir
La logica di serial grep è (non un commento) e (corrisponde a dir)
con questo eviti di testare un lookahead su ogni posizione:
/^(?:[^h]+|h++(?!ede))*+$/
equivalente a (per .net):
^(?>(?:[^h]+|h+(?!ede))*)$
Vecchia risposta:
/^(?>[^h]+|h+(?!ede))*$/
Il suddetto (?:(?!hede).)*
è ottimo perché può essere ancorato.
^(?:(?!hede).)*$ # A line without hede
foo(?:(?!hede).)*bar # foo followed by bar, without hede between them
Ma in questo caso sarebbe sufficiente quanto segue:
^(?!.*hede) # A line without hede
Questa semplificazione è pronta per l'aggiunta delle clausole "AND":
^(?!.*hede)(?=.*foo)(?=.*bar) # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar # Same
Ecco come lo farei:
^[^h]*(h(?!ede)[^h]*)*$
Preciso e più efficiente rispetto alle altre risposte. Implementa la tecnica dell'efficienza di "srotolamento del ciclo" di Friedl e richiede molto meno backtracking.
Se vuoi abbinare un carattere per negare una parola simile a negare la classe di caratteri:
Ad esempio, una stringa:
<?
$str="aaa bbb4 aaa bbb7";
?>
Non usare:
<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>
Uso:
<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>
L'avviso "(?!bbb)."
non è né guardare indietro né guardare avanti, è lookcurrent, ad esempio:
"(?=abc)abcde", "(?!abc)abcde"
Una variante, a mio avviso, più leggibile della risposta principale:
^(?!.*hede)
Fondamentalmente, "corrisponde all'inizio della riga se e solo se non contiene" hede "", quindi il requisito è stato tradotto quasi direttamente in regex.
Ovviamente è possibile avere più requisiti di guasto:
^(?!.*(hede|hodo|hada))
Dettagli: l'ancoraggio ^ assicura che il motore di regex non ritenti la corrispondenza in ogni posizione nella stringa, che corrisponderebbe a ogni stringa.
L'ancora ^ all'inizio ha lo scopo di rappresentare l'inizio della linea. Lo strumento grep abbina ogni riga una alla volta, nei contesti in cui stai lavorando con una stringa multilinea, puoi usare il flag "m":
/^(?!.*hede)/m # JavaScript syntax
o
(?m)^(?!.*hede) # Inline flag
L'OP non ha specificato o Tagil post per indicare il contesto (linguaggio di programmazione, editor, strumento) in cui verrà utilizzato il Regex.
Per me, a volte ho bisogno di farlo mentre modifico un file usando Textpad
.
Textpad
supporta alcune espressioni regolari, ma non supporta il lookahead o il lookbehind, quindi sono necessari alcuni passaggi.
Se sto cercando di mantenere tutte le righe che NON contengono la stringa hede
, lo farei in questo modo:
1. Cerca / sostituisci l'intero file per aggiungere un "Tag" univoco all'inizio di ogni riga contenente qualsiasi testo.
Search string:^(.)
Replace string:<@#-unique-#@>\1
Replace-all
2. Elimina tutte le righe che contengono la stringa
hede
( la stringa di sostituzione è vuota):
Search string:<@#-unique-#@>.*hede.*\n
Replace string:<nothing>
Replace-all
3. A questo punto, tutte le righe rimanenti NON contengono la stringa
hede
. Rimuovi il "Tag" univoco da tutte le righe (la stringa di sostituzione è vuota):
Search string:<@#-unique-#@>
Replace string:<nothing>
Replace-all
Ora hai il testo originale con tutte le righe contenenti la stringa hede
rimosse.
Se sto cercando di fare qualcos'altro solo su righe che NON contengono la stringa hede
, lo farei in questo modo:
1. Cerca / sostituisci l'intero file per aggiungere un "Tag" univoco all'inizio di ogni riga contenente qualsiasi testo.
Search string:^(.)
Replace string:<@#-unique-#@>\1
Replace-all
2. Per tutte le righe che contengono la stringa
hede
, rimuovi il "Tag" univoco:
Search string:<@#-unique-#@>(.*hede)
Replace string:\1
Replace-all
3. A questo punto, tutte le righe che iniziano con il "Tag" univoco, NON contengono la stringa
hede
. Ora posso fare qualcos'altro solo su quelle righe.
4. Quando ho finito, rimuovo il "Tag" univoco da tutte le righe (la stringa di sostituzione è vuota):
Search string:<@#-unique-#@>
Replace string:<nothing>
Replace-all
Poiché nessun altro ha dato una risposta diretta alla domanda che è stata posta , lo farò.
La risposta è che con POSIX grep
è impossibile soddisfare letteralmente questa richiesta:
grep "<Regex for 'doesn't contain hede'>" input
Il motivo è che POSIX grep
è richiesto solo per lavorare con le espressioni regolari di base , che semplicemente non sono abbastanza potenti per svolgere tale compito (non sono in grado di analizzare tutti i linguaggi regolari, a causa della mancanza di alternanza).
Tuttavia , GNU grep
implementa estensioni che lo consentono. In particolare, \|
è l'operatore di alternanza nell'implementazione dei BRE da parte di GNU. Se il tuo motore di espressioni regolari supporta l'alternanza, le parentesi e la stella di Kleene ed è in grado di ancorarsi all'inizio e alla fine della stringa, è tutto ciò di cui hai bisogno per questo approccio. Si noti tuttavia che i set negativi [^ ... ]
sono molto convenienti oltre a quelli, perché altrimenti è necessario sostituirli con un'espressione della forma (a|b|c| ... )
che elenchi ogni carattere che non è nel set, che è estremamente noioso ed eccessivamente lungo, ancora di più se l'intero set di caratteri è Unicode.
Grazie alla teoria del linguaggio formale, possiamo vedere come appare un'espressione del genere. Con GNU grep
, la risposta sarebbe qualcosa del tipo:
grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input
(trovato con Graal e alcune ulteriori ottimizzazioni fatte a mano).
Puoi anche utilizzare uno strumento che implementa le espressioni regolari estese , come egrep
, per sbarazzarti delle barre rovesciate:
egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input
Ecco uno script per testarlo (nota che genera un file testinput.txt
nella directory corrente). Molte delle espressioni presentate non superano questo test.
#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" # First four lines as in OP's testcase. cat > testinput.txt <<EOF hoho hihi haha hede h he ah head ahead ahed aheda ahede hhede hehede hedhede hehehehehehedehehe hedecidedthat EOF diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)
Nel mio sistema stampa:
Files /dev/fd/63 and /dev/fd/62 are identical
come previsto.
Per chi è interessato ai dettagli, la tecnica impiegata consiste nel convertire l'espressione regolare che corrisponde alla parola in un automa finito, quindi invertire l'automa cambiando ogni stato di accettazione in non accettazione e viceversa, e quindi riconvertendo la FA risultante in un'espressione regolare.
Come tutti hanno notato, se il tuo motore di espressioni regolari supporta il lookahead negativo, l'espressione regolare è molto più semplice. Ad esempio, con GNU grep:
grep -P '^((?!hede).)*$' input
Tuttavia, questo approccio ha lo svantaggio di richiedere un motore di espressioni regolari di backtracking. Ciò lo rende inadatto nelle installazioni che utilizzano motori di espressioni regolari sicuri come RE2 , motivo per cui preferire l'approccio generato in alcune circostanze.
Utilizzando l'eccellente libreria FormalTheory di Kendall Hopkins , scritta in PHP, che fornisce una funzionalità simile a Grail, e un semplificatore scritto da me stesso, sono stato in grado di scrivere un generatore online di espressioni regolari negative data una frase di input (solo alfanumerico e spazio caratteri attualmente supportati):http://www.formauri.es/personal/pgimeno/misc/non-match-regex/
Per hede
esso uscite:
^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$
che è equivalente a quanto sopra.
Dall'introduzione di ruby-2.4.1, possiamo usare il nuovo operatore assente nelle espressioni regolari di Ruby
dal doc. ufficiale
(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.
Quindi, nel tuo caso ^(?~hede)$
fa il lavoro per te
2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
=> ["hoho", "hihi", "haha"]
Attraverso il verbo PCRE (*SKIP)(*F)
^hede$(*SKIP)(*F)|^.*$
Ciò salta completamente la riga che contiene la stringa esatta hede
e corrisponde a tutte le righe rimanenti.
DEMO
Esecuzione delle parti:
Consideriamo la regex di cui sopra dividendola in due parti.
Parte prima del
|
simbolo. La parte non dovrebbe essere abbinata .^hede$(*SKIP)(*F)
Parte dopo il
|
simbolo. La parte dovrebbe essere abbinata .^.*$
PARTE 1
Il motore Regex inizierà la sua esecuzione dalla prima parte.
^hede$(*SKIP)(*F)
Spiegazione:
^
Afferma che siamo all'inizio.hede
Corrisponde alla stringahede
$
Afferma che siamo alla fine della riga.
Quindi la riga che contiene la stringa hede
verrebbe trovata. Una volta che il motore delle espressioni regolari vede il verbo seguente (*SKIP)(*F)
( Nota: puoi scrivere (*F)
come(*FAIL)
), salta e la corrispondenza fallisce. |
chiamata alterazione o operatore logico OR aggiunto accanto al verbo PCRE che in tornio corrisponde a tutti i confini esistenti tra ogni carattere su tutte le righe tranne la riga che contiene la stringa esatta hede
. Guarda la demo qui . Cioè, cerca di abbinare i caratteri della stringa rimanente. Ora la regex nella seconda parte verrà eseguita.
PARTE 2
^.*$
Spiegazione:
^
Afferma che siamo all'inizio. cioè, corrisponde a tutti gli inizi della riga tranne quello nellahede
riga. Guarda la demo qui ..*
Nella modalità Multiline,.
corrisponde a qualsiasi carattere tranne i caratteri di ritorno a capo o di ritorno a capo. E*
ripeterebbe il carattere precedente zero o più volte. Quindi.*
corrisponderebbe all'intera linea. Guarda la demo qui .Ehi, perché hai aggiunto. * Invece di. +?
Perché
.*
corrisponderebbe a una riga vuota ma.+
non a uno spazio vuoto. Vogliamo abbinare tutte le righe trannehede
, potrebbe esserci la possibilità di righe vuote anche nell'input. quindi devi usare al.*
posto di.+
..+
ripeterebbe il carattere precedente una o più volte. Vedi.*
corrisponde a una riga vuota qui .$
L'ancoraggio di fine linea non è necessario qui.
Un'altra opzione è quella di aggiungere un look-ahead positivo e verificare se si hede
trova in un punto qualsiasi della riga di input, lo negheremo, con un'espressione simile a:
^(?!(?=.*\bhede\b)).*$
con i confini delle parole.
L'espressione è spiegata nel pannello in alto a destra di regex101.com , se desideri esplorarla / semplificarla / modificarla, e in questo link , puoi vedere come corrisponderebbe ad alcuni input di esempio, se lo desideri.
Circuito RegEx
jex.im visualizza espressioni regolari:
Potrebbe essere più gestibile con due espressioni regolari nel codice, una per eseguire la prima corrispondenza e quindi, se corrisponde, eseguire la seconda espressione regolare per verificare i casi anomali che si desidera bloccare, ad esempio, ^.*(hede).*
quindi avere una logica appropriata nel codice.
OK, ammetto che questa non è davvero una risposta alla domanda pubblicata e potrebbe anche utilizzare un po 'più di elaborazione rispetto a una singola regex. Ma per gli sviluppatori che sono venuti qui alla ricerca di una soluzione rapida di emergenza per un caso anomalo, questa soluzione non dovrebbe essere trascurata.
Il linguaggio TXR supporta la negazione delle espressioni regolari .
$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)' Input
Un esempio più complicato: abbina tutte le righe che iniziano con a
e finiscono con z
, ma non contengono la sottostringa hede
:
$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az <- echoed
az
abcz <- echoed
abcz
abhederz <- not echoed; contains hede
ahedez <- not echoed; contains hede
ace <- not echoed; does not end in z
ahedz <- echoed
ahedz
La negazione dell'espressione regolare non è particolarmente utile da sola, ma quando hai anche l'intersezione, le cose si fanno interessanti, dato che hai un insieme completo di operazioni booleane sugli insiemi: puoi esprimere "l'insieme che corrisponde a questo, tranne per le cose che corrispondono a quello".
La seguente funzione ti aiuterà a ottenere l'output desiderato
<?PHP
function removePrepositions($text){ $propositions=array('/\bfor\b/i','/\bthe\b/i');
if( count($propositions) > 0 ) { foreach($propositions as $exceptionPhrase) { $text = preg_replace($exceptionPhrase, '', trim($text));
}
$retval = trim($text);
}
return $retval;
}
?>
^((?!hede).)*$
è una soluzione elegante, tranne che poiché consuma caratteri non sarai in grado di combinarla con altri criteri. Ad esempio, supponi di voler verificare la non presenza di "hede" e la presenza di "haha". Questa soluzione funzionerebbe perché non consumerà caratteri:
^(?!.*\bhede\b)(?=.*\bhaha\b)
Come utilizzare i verbi di controllo backtracking di PCRE per trovare una riga che non contiene una parola
Ecco un metodo che non ho mai visto usato prima:
/.*hede(*COMMIT)^|/
Come funziona
In primo luogo, cerca di trovare "hede" da qualche parte nella linea. In caso di successo, a questo punto, (*COMMIT)
dice al motore di non solo non tornare indietro in caso di guasto, ma anche di non tentare ulteriori corrispondenze in quel caso. Quindi, proviamo a trovare una corrispondenza con qualcosa che non può corrispondere (in questo caso, ^
).
Se una riga non contiene "hede", la seconda alternativa, un sottomodello vuoto, corrisponde correttamente alla stringa dell'oggetto.
Questo metodo non è più efficiente di un lookahead negativo, ma ho pensato di lanciarlo qui nel caso qualcuno lo trovasse elegante e trovasse un uso per altre applicazioni più interessanti.
Volevo aggiungere un altro esempio per se si sta cercando di abbinare una intera linea che contiene la stringa X , ma non contengono anche stringa Y .
Ad esempio, supponiamo di voler controllare se il nostro URL / stringa contiene " gustose prelibatezze ", a condizione che non contenga anche " cioccolato " da nessuna parte.
Questo pattern regex funzionerebbe (funziona anche in JavaScript)
^(?=.*?tasty-treats)((?!chocolate).)*$
(flag globali, multilinea nell'esempio)
Esempio interattivo: https://regexr.com/53gv4
Partite
(Questi URL contengono "leccornie" e inoltre non contengono "cioccolato")
- example.com/tasty-treats/strawberry-ice-cream
- example.com/desserts/tasty-treats/banana-pudding
- example.com/tasty-treats-overview
Non corrisponde
(Questi URL contengono "cioccolato" da qualche parte, quindi non corrispondono anche se contengono "prelibatezze")
- example.com/tasty-treats/chocolate-cake
- example.com/home-cooking/oven-roasted-chicken
- example.com/tasty-treats/banana-chocolate-fudge
- example.com/dessert/chocolate/tasty-treats
- example.com/chocolate/tasty-treats/desserts
Finché hai a che fare con le linee , contrassegna semplicemente le corrispondenze negative e scegli come target il resto .
In effetti, uso questo trucco con sed perché ^((?!hede).)*$
non sembra supportato da esso.
Per l'output desiderato
Contrassegna la corrispondenza negativa: (ad es. Righe con
hede
), utilizzando un carattere non incluso affatto nell'intero testo. Un'emoji potrebbe probabilmente essere una buona scelta per questo scopo.s/(.*hede)/🔒\1/g
Scegli come target il resto (le stringhe non contrassegnate: ad esempio le linee senza
hede
). Supponi di voler mantenere solo il target ed eliminare il resto (come vuoi):s/^🔒.*//g
Per una migliore comprensione
Supponi di voler eliminare il target :
Contrassegna la corrispondenza negativa: (ad es. Righe con
hede
), utilizzando un carattere non incluso affatto nell'intero testo. Un'emoji potrebbe probabilmente essere una buona scelta per questo scopo.s/(.*hede)/🔒\1/g
Scegli come target il resto (le stringhe non contrassegnate: ad esempio le linee senza
hede
). Supponi di voler eliminare il target :s/^[^🔒].*//g
Rimuovi il segno:
s/🔒//g
Una soluzione più semplice è utilizzare l'operatore not !
L' istruzione if dovrà corrispondere a "contiene" e non a "esclude".
var contains = /abc/;
var excludes =/hede/;
if(string.match(contains) && !(string.match(excludes))){ //proceed...
Credo che i progettisti di RegEx abbiano anticipato l'uso di non operatori.
Forse lo troverai su Google mentre provi a scrivere una regex in grado di abbinare segmenti di una riga (anziché intere righe) che non contengono una sottostringa. Mi hai chiesto un po 'di capire, quindi condividerò:
Data una stringa: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>
Voglio abbinare i <span>
tag che non contengono la sottostringa "bad".
/<span(?:(?!bad).)*?>
corrisponderà a <span class=\"good\">
e <span class=\"ugly\">
.
Notare che ci sono due gruppi (livelli) di parentesi:
- Quello più interno è per il lookahead negativo (non è un gruppo di cattura)
- Il più esterno è stato interpretato da Ruby come gruppo di cattura ma non vogliamo che sia un gruppo di cattura, quindi ho aggiunto?: All'inizio è e non è più interpretato come un gruppo di cattura.
Demo in Ruby:
s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]