Domanda Espressione regolare per abbinare una linea che non contiene una parola?


So che è possibile abbinare una parola e poi invertire le partite usando altri strumenti (ad es. grep -v). Tuttavia, mi piacerebbe sapere se è possibile abbinare le linee non contenere una parola specifica (ad es. hede) usando un'espressione regolare.

Ingresso:

hoho
hihi
haha
hede

Codice:

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

Output desiderato:

hoho
hihi
haha

3559


origine


risposte:


La nozione secondo cui la regex non supporta la corrispondenza inversa non è completamente vera. È possibile simulare questo comportamento utilizzando look-around negativi:

^((?!hede).)*$

L'espressione regolare sopra corrisponderà a qualsiasi stringa o linea senza un'interruzione di riga, non contenente la (sotto) stringa 'hede'. Come accennato, questo non è qualcosa che regex è "buono" in (o dovrebbe fare), ma comunque è possibile.

E se devi anche abbinare caratteri di interruzione di riga, usa il Modificatore DOT-ALL (il trailing s nel seguente schema):

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

o usarlo in linea:

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

(dove il /.../ sono i delimitatori regex, cioè non fanno parte del modello)

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

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

Spiegazione

Una stringa è solo una lista di n personaggi. Prima e dopo ogni carattere, c'è una stringa vuota. Quindi una lista di n i personaggi avranno 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 il esono le corde vuote. Il regex (?!hede). guarda avanti per vedere se non c'è sottostringa "hede" per essere visto, e se questo è il caso (quindi qualcos'altro è visto), allora il . (punto) corrisponderà a qualsiasi carattere tranne un'interruzione di riga. Vengono anche chiamati i guardiani di ampiezza zero-asserzioni perché non lo fanno consumare qualsiasi personaggio Asseriscono / convalidano solo qualcosa.

Quindi, nel mio esempio, ogni stringa vuota viene prima convalidata per vedere se non c'è "hede" avanti, prima che un personaggio venga consumato dal . (punto). Il regex (?!hede). lo farà solo una volta, quindi è racchiuso 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 sia consumato: ^((?!hede).)*$

Come puoi vedere, l'input "ABhedeCD" fallirà perché su e3, la regex (?!hede) fallisce (lì è  "hede" avanti!).


4851



Si noti che la soluzione a non iniziare con “Hede”:

^(?!hede).*$

è generalmente molto più efficiente della soluzione a non contenere “Hede”:

^((?!hede).)*$

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


603



Se lo stai solo usando per grep, puoi usarlo grep -v hede per ottenere tutte le linee che non contengono hede.

ETA Oh, rileggendo la domanda, grep -v è probabilmente ciò che intendevi per "opzioni degli strumenti".


163



Risposta:

^((?!hede).)*$

Spiegazione:

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

hedela tua corda,

) fine del look-ahead, . qualsiasi carattere tranne \ n,
)* fine di \ 1 (Nota: poiché stai usando un quantificatore su questa cattura, solo l'ULTIMA ripetizione del modello catturato verrà memorizzata in \ 1)
$ prima di un \ n e la fine della stringa opzionale


121



Le risposte date sono perfette, solo un punto accademico:

Espressioni regolari nel significato di scienze informatiche teoriche NON SONO ABILI Fai cosi. Per loro doveva sembrare qualcosa del genere:

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

Questo fa solo una partita COMPLETA. Farlo per sub-partite sarebbe anche più imbarazzante.


89



Se vuoi il test regex a solo fallire se il intera stringa partite, il seguente funzionerà:

^(?!hede$).*

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

Certo, se stai cercando esatto l'uguaglianza, una soluzione generale migliore in questo caso è controllare l'uguaglianza delle stringhe, ad es.

myStr !== 'foo'

Potresti anche mettere la negazione al di fuori il test se hai bisogno di alcune caratteristiche regex (qui, insensibilità al caso e corrispondenza di intervallo):

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

La soluzione regex in alto può essere utile, tuttavia, in situazioni in cui è richiesto un test di regex positivo (forse da un'API).


48



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


47



FWIW, poiché i linguaggi regolari (ovvero i linguaggi razionali) sono chiusi in complemento, è sempre possibile trovare un'espressione regolare (alias espressione razionale) che nega un'altra espressione. Ma non molti strumenti implementano questo.

Vcsn supporta questo operatore (che denota {c}, postfix).

Per prima cosa definisci il tipo delle tue espressioni: le etichette sono lettere (lal_char) da scegliere a a z per 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, respinto.

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

The corresponding automaton

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 + è solitamente denotato |, \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 Quie prova Vcsn online .


43



benchmark

Ho deciso di valutare alcune delle opzioni presentate e confrontare le loro prestazioni, nonché 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 esecuzioni - Più grande numero = 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 è stato possibile testare le soluzioni P1 e P2.

Sommario:

Ho provato a testare la maggior parte delle soluzioni proposte, alcune ottimizzazioni sono possibili per determinate parole. Ad esempio, se le prime due lettere della stringa di ricerca non sono uguali, è possibile estendere la risposta 03 a ^(?>[^R]+|R+(?!egex Hero))*$ risultando in un piccolo guadagno in termini di prestazioni.

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


38



Con lookahead negativo, l'espressione regolare può corrispondere a qualcosa che non contiene pattern specifici. Questo è stato risposto e spiegato da Bart Kiers. Ottima spiegazione!

Tuttavia, con la risposta di Bart Kiers, la parte lookahead testerà da 1 a 4 caratteri in anticipo mentre combacia con qualsiasi singolo carattere. Possiamo evitarlo e lasciare che la parte lookahead controlli l'intero testo, assicurati che non ci sia 'hede', e quindi la parte normale (. *) Può mangiare tutto il testo tutto in una volta.

Ecco la regex migliorata:

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

Nota che il quantificatore (*?) Pigro nella parte lookahead negativa è facoltativo, 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; altrimenti, il quantificatore avido sarà più veloce. Tuttavia se "hede" non è presente, entrambi sarebbero uguali lenti.

Ecco il codice demo.

Per ulteriori informazioni su lookahead, consulta l'ottimo articolo: Padroneggiare Lookahead e Lookbehind.

Inoltre, si prega di controllare 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()
);

37



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 (dir corrisponde)


30