Espressione regolare per abbinare una riga che non contiene una parola


4294

So che è possibile abbinare una parola e quindi invertire le partite usando altri strumenti (ad es grep -v.). Tuttavia, è possibile abbinare le righe che non contengono una parola specifica, ad esempio hedeusando un'espressione regolare?

Ingresso:

hoho
hihi
haha
hede

Codice:

grep "<Regex for 'doesn't contain hede'>" input

Uscita desiderata:

hoho
hihi
haha

85
Probabilmente con un paio d'anni di ritardo, ma cosa c'è che non va ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*:? L'idea è semplice Continua la corrispondenza finché non vedi l'inizio della stringa indesiderata, quindi esegui la corrispondenza solo nei casi N-1 in cui la stringa è incompleta (dove N è la lunghezza della stringa). Questi casi N-1 sono "h seguito da non-e", "seguito da non-d" e "hed seguito da non-e". Se sei riuscito a passare questi N-1 dei casi, è successo non ha corrispondono con la stringa indesiderata in modo da poter iniziare la ricerca di [^h]*nuovo
stevendesu

323
@stevendesu: prova questo per "una parola molto molto lunga" o anche per una mezza frase ancora migliore. Divertiti a digitare. A proposito, è quasi illeggibile. Non so l'impatto sulle prestazioni.
Peter Schuetze,

13
@PeterSchuetze: Certo non è carino per parole molto molto lunghe, ma è una soluzione praticabile e corretta. Anche se non ho eseguito test sull'esecuzione, non immagino che sia troppo lento poiché la maggior parte di queste ultime regole vengono ignorate fino a quando non vedi una h (o la prima lettera della parola, frase, ecc.). E potresti facilmente generare la stringa regex per stringhe lunghe usando la concatenazione iterativa. Se funziona e può essere generato rapidamente, la leggibilità è importante? Ecco a cosa servono i commenti.
Stevendesu,

57
@stevendesu: sono ancora più tardi, ma quella risposta è quasi completamente sbagliata. per prima cosa, richiede che l'oggetto contenga "h" che non dovrebbe, dato che l'attività è "linee di corrispondenza che [non] contengono una parola specifica". supponiamo che tu intendessi rendere facoltativo il gruppo interno e che il modello sia ancorato: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ ciò fallisce quando le istanze di "hede" sono precedute da istanze parziali di "hede" come in "hhede".
jaytea,

8
Questa domanda è stata aggiunta alle FAQ sulle espressioni regolari di Stack Overflow , in "Advanced Regex-Fu".
aliteralmind

Risposte:


5895

L'idea che regex non supporti la corrispondenza inversa non è del tutto vera. È possibile simulare questo comportamento utilizzando gli sguardi negativi:

^((?!hede).)*$

La regex sopra corrisponderà a qualsiasi stringa o riga senza interruzione di riga, non contenente la (sotto) stringa 'hede'. Come già detto, questo non è qualcosa regex è "buono" a (o dovrebbe fare), ma ancora, è possibile.

E se è necessario abbinare anche i caratteri di interruzione di riga, utilizzare il modificatore DOT-ALL (il trailing snel seguente modello):

/^((?!hede).)*$/s

o usalo in linea:

/(?s)^((?!hede).)*$/

(dove /.../sono i delimitatori regex, ovvero non fanno parte del modello)

Se il modificatore DOT-ALL non è disponibile, è possibile simulare lo stesso comportamento con la classe di caratteri [\s\S]:

/^((?!hede)[\s\S])*$/

Spiegazione

Una stringa è solo un elenco di ncaratteri. Prima e dopo ogni carattere, c'è una stringa vuota. Quindi un elenco di ncaratteri avrà n+1stringhe 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 esono le stringhe vuote. Il regex (?!hede).guarda avanti per vedere se non ci sono sottostringhe "hede"da vedere, e se questo è il caso (così si vede qualcos'altro), allora 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 personaggio. Asseriscono / convalidano solo qualcosa.

Quindi, nel mio esempio, ogni stringa vuota viene prima convalidata per vedere se non c'è "hede"davanti, 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!).


26
Non direi che questo è qualcosa di regex in cui non va bene. La convenienza di questa soluzione è abbastanza ovvia e il successo in termini di prestazioni rispetto a una ricerca programmatica sarà spesso irrilevante.
Archimaredes,

29
A rigor di termini, un aspetto negativo rende l'espressione regolare non regolare.
Peter K,

55
@PeterK, certo, ma è così, non MathOverflow o CS-Stackexchange. Le persone che fanno una domanda qui sono generalmente alla ricerca di una risposta pratica. La maggior parte delle librerie o strumenti (come grepquelli menzionati dall'OP) con supporto regex hanno tutti caratteristiche che li rendono non regolari in senso teorico.
Bart Kiers,

19
@Bart Kiers, nessuna offesa per la tua risposta, proprio questo abuso di terminologia mi irrita un po '. La parte davvero confusa qui è che le espressioni regolari in senso stretto possono fare molto quello che OP vuole, ma il linguaggio comune per scriverle non lo consente, il che porta a soluzioni (matematicamente brutte) come le anticipazioni. Si prega di vedere questa risposta qui sotto e il mio commento lì per il modo corretto (teoricamente allineato) di farlo. Inutile dire che funziona più velocemente su input di grandi dimensioni.
Peter K,

17
Nel caso in cui ti sei mai chiesto come farlo in vim:^\(\(hede\)\@!.\)*$
baldrs

739

Nota che la soluzione non inizia con "hede" :

^(?!hede).*$

è generalmente molto più efficiente della soluzione per non contenere "hede" :

^((?!hede).)*$

Il primo cerca "hede" solo nella prima posizione della stringa di input, piuttosto che in ogni posizione.


5
Grazie, l'ho usato per convalidare che la stringa non contiene squence di cifre ^ ((?! \ D {5,}).) *
Samih A

2
Ciao! Non riesco a comporre non finisce con "hede" regex. Puoi aiutarci?
Aleks Ya,

1
@AleksYa: basta usare la versione "contenere" e includere l'ancoraggio finale nella stringa di ricerca: cambiare la stringa in "non abbinare" da "hede" a "hede $"
Nyerguds,

2
@AleksYa: la versione non finisce potrebbe essere fatto utilizzando lookbehind negativo come: (.*)(?<!hede)$. Anche la versione di @Nyerguds funzionerebbe ma manca completamente il punto sulle prestazioni menzionato nella risposta.
thisismydesign

5
Perché dicono così tante risposte ^((?!hede).)*$? Non è più efficiente da usare ^(?!.*hede).*$? Fa la stessa cosa ma in meno passaggi
JackPRead

208

Se lo stai usando per grep, puoi usare grep -v hedetutte le righe che non contengono hede.

ETA Oh, rileggere la domanda, grep -vè probabilmente quello che intendevi per "opzioni degli strumenti".


22
Suggerimento: per filtrare progressivamente ciò che non si desidera: grep -v "hede" | grep -v "hihi" | ...eccetera.
Olivier Lalonde

51
O usando solo un processogrep -v -e hede -e hihi -e ...
Olaf Dietsche,

15
O semplicemente grep -v "hede\|hihi":)
Putnik,

2
Se hai molti pattern che vuoi filtrare, inseriscili in un file e grep -vf pattern_file file
usali

4
O semplicemente egrepo grep -Ev "hede|hihi|etc"per evitare la fuga imbarazzante.
Amit Naidu,

161

Risposta:

^((?!hede).)*$

Spiegazione:

^l'inizio della stringa, (raggruppa e acquisisci a \ 1 (0 o più volte (corrispondente alla maggior quantità possibile)),
(?!guarda avanti per vedere se non c'è,

hede la tua corda,

)fine del look-ahead, .qualsiasi carattere tranne \ n,
)*fine di \ 1 (Nota: poiché si sta utilizzando un quantificatore in questa acquisizione, solo l'ULTIMA ripetizione del modello acquisito verrà memorizzata in \ 1)
$prima di un \ n facoltativo, e la fine della stringa


14
fantastico che ha funzionato per me nel sublime testo 2 usando più parole ' ^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'
Damodar Bashyal,

3
@DamodarBashyal So che sono piuttosto in ritardo qui, ma potresti rimuovere del tutto il secondo termine e otterrai esattamente gli stessi risultati
forresthopkinsa,

99

Le risposte fornite sono perfettamente valide, solo un punto accademico:

Le espressioni regolari nel significato di informatica teorica NON SONO IN GRADO di farlo in questo modo. Per loro doveva essere simile a questo:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

Questo fa solo una partita COMPLETA. Farlo per le partite secondarie sarebbe persino più imbarazzante.


1
È importante notare che questo utilizza solo espressioni regolari POSIX.2 di base e quindi mentre terse è più portatile per quando PCRE non è disponibile.
Steve-o

5
Sono d'accordo. Molte se non la maggior parte delle espressioni regolari non sono lingue regolari e non possono essere riconosciute da automi limitati.
ThomasMcLeod,

@ThomasMcLeod, Hades32: È nei regni di ogni possibile linguaggio regolare essere in grado di dire " non " e " e " così come " o " di un'espressione come " (hede|Hihi)"? (Questa forse è una domanda per CS.)
James Haigh,

7
@JohnAllen: ME !!! ... Beh, non la regex reale ma il riferimento accademico, che è anche strettamente correlato alla complessità computazionale; Fondamentalmente i PCRE non possono garantire la stessa efficienza delle espressioni regolari POSIX.
James Haigh,

4
Scusate, questa risposta non funziona, abbinerà anche lui e anche parzialmente (il secondo tempo)
Falco

60

Se si desidera che il test regex fallisca solo se l' intera stringa corrisponde, funzionerà quanto segue:

^(?!hede$).*

es. - Se vuoi consentire tutti i valori tranne "foo" (cioè "foofoo", "barfoo" e "foobar" passeranno, ma "foo" fallirà), usa: ^(?!foo$).*

Naturalmente, se stai verificando l' uguaglianza esatta , una soluzione generale migliore in questo caso è quella di verificare l'uguaglianza delle stringhe, vale a dire

myStr !== 'foo'

Potresti anche mettere la negazione fuori dal test se hai bisogno di funzionalità regex (qui, insensibilità al maiuscolo e intervallo):

!/^[a-f]oo$/i.test(myStr)

La soluzione regex nella parte superiore di questa risposta può essere utile, tuttavia, nelle situazioni in cui è richiesto un test regex positivo (forse da un'API).


che dire di spazi bianchi finali? Ad esempio, se voglio che il test fallisca con la stringa " hede "?
Egor,

@eagor la \sdirettiva corrisponde a un singolo personaggio degli spazi bianchi
Roy Tinker,

grazie, ma non sono riuscito ad aggiornare regex per farlo funzionare.
Egor

2
@eagor:^(?!\s*hede\s*$).*
Roy Tinker,

52

FWIW, poiché le lingue regolari (ovvero le lingue razionali) sono chiuse in modo complementare, è sempre possibile trovare un'espressione regolare (aka espressione razionale) che nega un'altra espressione. Ma non molti strumenti implementano questo.

Vcsn supporta questo operatore (che indica {c}, postfisso).

Per prima cosa definisci il tipo delle tue espressioni: le etichette sono letter ( lal_char) tra cui scegliere aad zesempio (definire l'alfabeto quando si lavora con la complementazione è, ovviamente, molto importante), e il "valore" calcolato per ogni parola è solo un booleano : truela parola è accettata false, respinta.

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

L'automa corrispondente

infine, converti questo automa in una semplice espressione.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

dove di +solito è indicato |, \eindica 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 .


6
Vero, ma brutto e fattibile solo per piccoli set di caratteri. Non vuoi farlo con le stringhe Unicode :-)
reinierpost

Ci sono più strumenti che lo consentono, uno dei più impressionanti è Ragel . Lì sarebbe scritto come (qualsiasi * - ('hehe' qualsiasi *)) per la corrispondenza allineata all'inizio o (qualsiasi * - ('hehe' qualsiasi *)) per non allineati.
Peter K,

1
@reinierpost: perché è brutto e qual è il problema con Unicode? Non posso essere d'accordo su entrambi. (Non ho esperienza con vcsn, ma con DFA).
Peter K,

3
@PedroGimeno Quando ti sei ancorato, ti sei assicurato di mettere prima questa regex in parentesi? Altrimenti le precedenti tra ancore e |non giocheranno bene. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'.
Akim

1
Penso che valga la pena notare che questo metodo è per abbinare le linee che non sono la parola 'hede', piuttosto che le linee piuttosto che non contengono la parola 'hede' che è ciò che l'OP ha richiesto. Vedi la mia risposta per quest'ultima.
Pedro Gimeno,

51

Ecco una buona spiegazione del perché non è facile negare una regex arbitraria. Devo concordare con le altre risposte, però: se questa è qualcosa di diverso da una domanda ipotetica, allora una regex non è la scelta giusta qui.


10
Alcuni strumenti, e in particolare mysqldumpslow, offrono solo questo modo per filtrare i dati, quindi in tal caso, trovare un regex per farlo è la soluzione migliore oltre a riscrivere lo strumento (varie patch per questo non sono state incluse da MySQL AB / Sun / Oracle.
MGF

1
Esattamente analogo alla mia situazione. Il motore di template di Velocity utilizza espressioni regolari per decidere quando applicare una trasformazione (escape html) e voglio che funzioni sempre TRANNE in una situazione.
Henno Vermeulen,

1
Quale alternativa c'è? Non ho mai incontrato nulla che potesse fare una corrispondenza di stringa precisa oltre a regex. Se OP utilizza un linguaggio di programmazione, potrebbero esserci altri strumenti disponibili, ma se sta usando non scrivendo codice, probabilmente non c'è altra scelta.
kingfrito_5005,

2
Uno dei molti scenari non ipotetici in cui un regex è la migliore scelta disponibile: mi trovo in un IDE (Android Studio) che mostra l'output del registro e gli unici strumenti di filtro forniti sono: stringhe semplici e regex. Cercare di farlo con semplici stringhe sarebbe un completo fallimento.
LarsH,

48

Con lookahead negativo, l'espressione regolare può corrispondere a qualcosa che non contiene un modello specifico. Questa è la risposta e spiegata da Bart Kiers. Grande spiegazione!

Tuttavia, con la risposta di Bart Kiers, la parte lookahead metterà alla prova da 1 a 4 caratteri in avanti mentre si abbina un singolo personaggio. Possiamo evitarlo e lasciare che la parte lookahead verifichi l'intero testo, assicurandoci che non ci sia 'hede', e quindi la parte normale (. *) Può mangiare l'intero testo in una sola volta.

Ecco la regex migliorata:

/^(?!.*?hede).*$/

Si noti che il quantificatore (*?) Pigro nella parte negativa del lookahead è facoltativo, è possibile utilizzare invece (*) quantificatore avido, a seconda dei dati: se 'hede' è presente e nella metà iniziale del testo, il quantificatore pigro può essere più veloce; altrimenti, 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 e Lookbehind .

Inoltre, controlla RegexGen.js , un generatore di espressioni regolari JavaScript che aiuta a costruire espressioni regolari complesse. Con RegexGen.js, puoi costruire il 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()
);

3
quindi per controllare semplicemente se la stringa data non contiene str1 e str2:^(?!.*(str1|str2)).*$
S.Serpooshan

1
Sì, oppure puoi usare il quantificatore pigro: a ^(?!.*?(?:str1|str2)).*$seconda dei tuoi dati. Aggiunto il ?:dato che non è necessario catturarlo.
amobiz,

Questa è di gran lunga la risposta migliore per un fattore di 10xms. Se hai aggiunto il tuo codice jsfiddle e i risultati alla risposta, le persone potrebbero notarlo. Mi chiedo perché la versione pigra sia più veloce della versione golosa quando non c'è hede. Non dovrebbero impiegare la stessa quantità di tempo?
user5389726598465

Sì, impiegano lo stesso tempo poiché entrambi testano l'intero testo.
amobiz,

41

Punti di riferimenti

Ho deciso di valutare alcune delle Opzioni presentate e di confrontare le loro prestazioni, nonché di utilizzare alcune nuove funzionalità. Benchmarking su .NET Regex Engine: http://regexhero.net/tester/

Testo di riferimento:

Le prime 7 righe non devono corrispondere, poiché contengono l'espressione cercata, mentre le 7 righe inferiori devono 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 corse - Numero più grande = 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 certe 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 guadagno in termini di prestazioni.

Ma la soluzione complessiva più leggibile e più saggia in termini di prestazioni sembra essere 05 usando un'istruzione condizionale o 04 con il quantificatore possessivo. Penso che le soluzioni Perl dovrebbero essere ancora più veloci e più facilmente leggibili.


5
Dovresti ^(?!.*hede)anche tempo . /// Inoltre, probabilmente è meglio classificare le espressioni per il corpus corrispondente e il corpus non corrispondente separatamente perché di solito è un caso in cui la maggior parte delle righe corrisponde o la maggior parte delle righe no.
ikegami,

32

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 grep seriale è (non un commento) e (corrisponde a dir)


2
Penso che stia chiedendo la versione regex digrep -v
Angel.King.47,

9
Questo è pericoloso. Manca anche linee comegood_stuff #comment_stuff
Xavi Montero

29

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))*$/

7
Buon punto; Sono sorpreso che nessuno abbia menzionato prima questo approccio. Tuttavia, quel regex particolare è soggetto a catastrofici backtracking quando applicato a testo che non corrisponde. Ecco come lo farei:/^[^h]*(?:h+(?!ede)[^h]*)*$/
Alan Moore,

... oppure puoi semplicemente rendere possessivi tutti i quantificatori. ;)
Alan Moore,

@Alan Moore - Anche io sono sorpreso. Ho visto il tuo commento (e la migliore regex nella pila) qui solo dopo aver pubblicato lo stesso schema in una risposta di seguito.
ridgerunner,

@ridgerunner, non deve essere il migliore. Ho visto benchmark in cui la risposta migliore ha prestazioni migliori. (Ne sono rimasto sorpreso).
Qtax,

23

Quanto (?:(?!hede).)*sopra è 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:

^(?!.*hede)                    # A line without hede

Questa semplificazione è pronta per l'aggiunta di clausole "AND":

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

20

Ecco come lo farei:

^[^h]*(h(?!ede)[^h]*)*$

Accurato e più efficiente delle altre risposte. Implementa la tecnica di efficienza "svolgendo il ciclo" di Friedl e richiede molto meno backtracking.


17

Se si desidera abbinare un personaggio 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);
?>

Si noti che "(?!bbb)."non è né lookbehind né lookahead, è lookcurrent, ad esempio:

"(?=abc)abcde", "(?!abc)abcde"

3
Non c'è "lookcurrent" in perl regexp's. Questo è davvero un lookahead negativo (prefisso (?!). Il prefisso lookahead positivo sarebbe (?=mentre i prefissi lookbehind corrispondenti sarebbero (?<!e (?<=rispettivamente. Un lookahead significa che leggi i personaggi successivi (quindi "avanti") senza consumarli. Un lookbehind significa che controlli i personaggi che sono già stati consumati.
Didier L

14

Una, a mio avviso, variante più leggibile della risposta principale:

^(?!.*hede)

Fondamentalmente, "abbina all'inizio della riga se e solo se non ha" hede "al suo interno", quindi il requisito tradotto quasi direttamente in regex.

Naturalmente, è possibile avere più requisiti di errore:

^(?!.*(hede|hodo|hada))

Dettagli: L'ancoraggio ^ assicura che il motore regex non riprovi la corrispondenza in ogni posizione della stringa, che corrisponderebbe a ogni stringa.

L'ancora ^ all'inizio è pensata per rappresentare l'inizio della linea. Lo strumento grep fa corrispondere ciascuna riga una alla volta, in contesti in cui lavori con una stringa multilinea, puoi usare il flag "m":

/^(?!.*hede)/m # JavaScript syntax

o

(?m)^(?!.*hede) # Inline flag

Ottimo esempio con negazione multipla.
Peter Parada,

Una differenza dalla risposta principale è che questo non corrisponde a nulla, e che corrisponde a tutta la linea se senza "hede"
Z. Khullah,

13

L'OP non ha specificato o Tagil post per indicare il contesto (linguaggio di programmazione, editor, strumento) all'interno del quale verrà utilizzato Regex.

Per me, a volte ho bisogno di farlo mentre modifico un file usando Textpad.

Textpad supporta alcuni Regex, ma non supporta lookahead o lookbehind, quindi richiede alcuni passaggi.

Se sto cercando di conservare 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 che contengono la stringa hederimossa.


Se sto cercando di fare qualcos'altro solo per 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. Per tutte le righe che contengono la stringa hede, rimuovere 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 il mio Something Else solo con quelle righe.

4. Al termine, rimuovo il "Tag" univoco da tutte le righe (la stringa di sostituzione è vuota):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

12

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 grepdeve funzionare solo con le espressioni regolari di base , che semplicemente non sono abbastanza potenti per eseguire tale compito (non sono in grado di analizzare le lingue regolari, a causa della mancanza di alternanza e parentesi).

Tuttavia, GNU grepimplementa estensioni che lo consentono. In particolare, \|è l'operatore di alternanza nell'implementazione di BRE delle GNU \(e \)sono le parentesi. Se il tuo motore di espressioni regolari supporta l'alternanza, le espressioni di parentesi negative, le parentesi e la stella di Kleene ed è in grado di ancorare all'inizio e alla fine della stringa, questo è tutto ciò che serve per questo approccio. Si noti tuttavia che gli insiemi negativi [^ ... ]sono molto convenienti in aggiunta a quelli, perché altrimenti, è necessario sostituirli con un'espressione del modulo (a|b|c| ... )che elenca tutti i caratteri che non sono nell'insieme, che è estremamente noioso e eccessivamente lungo, ancor più se l'intero set di caratteri è Unicode.

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 eliminare le 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.txtnella directory corrente):

#!/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 coloro che sono interessati ai dettagli, la tecnica utilizzata è quella di 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, quindi riconvertendo il FA risultante in un'espressione regolare.

Infine, come tutti hanno notato, se il tuo motore di espressione regolare supporta lookahead negativo, questo semplifica molto l'attività. Ad esempio, con GNU grep:

grep -P '^((?!hede).)*$' input

Aggiornamento: Di recente ho trovato l'eccellente libreria FormalTheory di Kendall Hopkins , scritta in PHP, che offre una funzionalità simile a Grail. Usandolo e un semplificatore scritto da me stesso, sono stato in grado di scrivere un generatore online di espressioni regolari negative con una frase di input (solo caratteri alfanumerici e spaziali attualmente supportati): http://www.formauri.es/personal/ pgimeno / misc / non-match-regex /

Per hedeesso produce:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

che equivale a quanto sopra.


11

Dall'introduzione di ruby-2.4.1, possiamo usare il nuovo operatore assente nelle espressioni regolari di Ruby

dal documento 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"]

9

Tramite il verbo PCRE (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

Ciò salta completamente la riga che contiene la stringa esatta hedee corrisponde a tutte le righe rimanenti.

DEMO

Esecuzione delle parti:

Consideriamo il regex sopra suddividendolo in due parti.

  1. Parte prima del |simbolo. La parte non deve essere abbinata .

    ^hede$(*SKIP)(*F)
  2. Parte dopo il |simbolo. La parte deve 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 stringa hede
  • $ Afferma che siamo alla fine della linea.

Quindi la riga che contiene la stringa hedeverrebbe abbinata. Quando il motore regex vede il seguente verbo (*SKIP)(*F)( Nota: potresti scrivere (*F)come(*FAIL) ), salta e fa fallire la corrispondenza. |chiamato alterazione o operatore logico OR aggiunto accanto al verbo PCRE che inturn corrisponde a tutti i confini esistenti tra ogni singolo carattere su tutte le righe tranne la riga contiene la stringa esatta hede. Guarda la demo qui . Cioè, cerca di abbinare i caratteri della stringa rimanente. Ora la regex nella seconda parte verrebbe eseguita.

PARTE 2

^.*$

Spiegazione:

  • ^ Afferma che siamo all'inizio. cioè, corrisponde a tutti gli inizi della linea tranne quello nella hedelinea. Guarda la demo qui .
  • .*Nella modalità multilinea, .corrisponderebbe a qualsiasi carattere tranne i caratteri di ritorno a capo o 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 corrisponderà a uno spazio vuoto. Vogliamo abbinare tutte le linee tranne hede, ci può essere una possibilità di linee vuote anche nell'input. quindi devi usare .*invece di .+. .+ripeterebbe il personaggio precedente una o più volte. Vedi le .*corrispondenze una riga vuota qui .

  • $ L'ancoraggio di fine linea non è necessario qui.


7

Potrebbe essere più gestibile per due regex nel codice, uno per eseguire la prima corrispondenza, e quindi se corrisponde eseguire la seconda regex per verificare i casi anomali che si desidera bloccare, ad esempio ^.*(hede).*avere una logica appropriata nel codice.

OK, ammetto che questa non è in realtà una risposta alla domanda postata 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 rapida soluzione di emergenza per un caso anomalo, questa soluzione non deve essere trascurata.


6

Un'altra opzione è quella di aggiungere un aspetto positivo e verificare se si hehetrova in qualsiasi punto della riga di input, quindi lo negheremmo, con un'espressione simile a:

^(?!(?=.*\bhede\b)).*$

con limiti di parole.


L'espressione è spiegata nel pannello in alto a destra di regex101.com , se si desidera esplorarlo / semplificarlo / modificarlo, e in questo collegamento , è possibile vedere come corrisponderebbe con alcuni input di esempio, se lo si desidera.


Circuito RegEx

jex.im visualizza le espressioni regolari:

inserisci qui la descrizione dell'immagine


5

Il linguaggio TXR supporta la negazione regex.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

Un esempio più complicato: abbina tutte le righe che iniziano ae 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 di Regex non è particolarmente utile da sola, ma quando hai anche un incrocio, le cose diventano interessanti, dato che hai un set completo di operazioni booleane: puoi esprimere "l'insieme che corrisponde a questo, tranne per le cose che corrispondono a quello".


Si noti che è anche la soluzione per regex basato su ElasticSearch Lucene.
Wiktor Stribiżew,

4

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;
    }


?>

2

^ ((?! hede).) * $ è una soluzione elegante, tranne perché consuma caratteri che non potrai combinare con altri criteri. Ad esempio, supponi di voler verificare la non presenza di "hede" e la presenza di "haha". Questa soluzione funzionerebbe perché non consumerebbe caratteri:

^ (?!. \ bhede \ b) (? =. \ bhaha \ b)


1

Come usare i verbi di controllo backtracking di PCRE per abbinare 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)indica al motore di non solo tornare indietro in caso di guasto, ma anche di non tentare ulteriori corrispondenze in quel caso. Quindi, proviamo ad abbinare qualcosa che non può corrispondere (in questo caso ^).

Se una riga non contiene "hede", la seconda alternativa, un sottotatro vuoto, corrisponde correttamente alla stringa dell'oggetto.

Questo metodo non è più efficiente di uno sguardo negativo, ma ho pensato di lanciarlo qui nel caso in cui qualcuno lo trovasse elegante e lo trovasse utile per altre applicazioni più interessanti.


0

Una soluzione più semplice è usare l'operatore not !

L' istruzione if dovrà corrispondere a "contiene" e non corrispondere 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.


0

Forse lo troverai su Google mentre provi a scrivere una regex in grado di abbinare segmenti di una linea (al contrario di intere righe) che non contengono una sottostringa. Mi ci è voluto un po 'per 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 "cattiva".

/<span(?:(?!bad).)*?>corrisponderà <span class=\"good\">e <span class=\"ugly\">.

Si noti che esistono due serie (livelli) di parentesi:

  • Quello più interno è per il lookahead negativo (non è un gruppo di acquisizione)
  • Il più esterno è stato interpretato da Ruby come gruppo di acquisizione, ma non vogliamo che sia un gruppo di acquisizione, quindi ho aggiunto?: All'inizio e non viene più interpretato come un gruppo di acquisizione.

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\">"]

0

Con ConyEdit , è possibile utilizzare la riga di comando cc.gl !/hede/per ottenere righe che non contengono la corrispondenza regex oppure utilizzare la riga di comando cc.dl /hede/per eliminare le righe che contengono la corrispondenza regex. Hanno lo stesso risultato.


0

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 verificare se il nostro URL / stringa contiene " gustosi dolcetti ", purché non contenga " cioccolato " da nessuna parte.

Questo modello regex funzionerebbe (funziona anche in JavaScript)

^(?=.*?tasty-treats)((?!chocolate).)*$

(esempio globale, bandiere multilinea)

Esempio interattivo: https://regexr.com/53gv4

fiammiferi

(Questi URL contengono "prelibatezze" e 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 combaciano 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/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts
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.