2009-11-17 10 views
40

Nella mia directory home ho una cartella drupal-6.14 che contiene la piattaforma Drupal.Espressione normale lookahead negativo

Da questa directory Io uso il seguente comando:

find drupal-6.14 -type f -iname '*' | grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*' | xargs tar -czf drupal-6.14.tar.gz 

Che questo comando non fa altro che gzip la cartella drupal-6.14, escludendo tutte le sottocartelle della Drupal 6.14/siti/tranne siti/tutti e siti/default, che include.

La mia domanda è l'espressione regolare:

grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*' 

L'espressione lavora per escludere tutte le cartelle che voglio esclusi, ma io non riesco a capire perché.

Si tratta di un compito comune utilizzando le espressioni regolari per

hanno tutte le stringhe, ad eccezione di quelli che Non contengono sotto-regola x. O in altre parole, annullando un subpattern.

I (credo) ho capito che la strategia generale per risolvere questi problemi è l'uso di lookaheads negative, ma non ho mai capito a un livello soddisfacente di come aspetto positivo e negativo (avanti/indietro) s lavoro.

Nel corso degli anni, ho letto molti siti Web su di essi. I manuali di regex di PHP e Python, altre pagine come http://www.regular-expressions.info/lookaround.html e così via, ma non ho mai avuto il veramente hanno avuto una solida comprensione di loro.

Qualcuno potrebbe spiegare come funziona e forse fornire esempi simili che farebbero cose simili?

- aggiornare uno:

Per quanto riguarda la risposta di Andomar: può una doppia lookahead negativo essere più sinteticamente espresso come una singola istruzione lookahead positivo:

IE è:

'drupal-6.14/(?!sites(?!/all|/default)).*' 

equivalente a:

'drupal-6.14/(?=sites(?:/all|/default)).*' 

???

- Aggiornamento Due:

Come per @andomar e @alan Moore - non è possibile scambiare doppio lookahead negativo per lookahead positivo.

risposta

84

Un lookahead negativo dice , in questa posizione, la seguente regex non può corrispondere.

Facciamo un esempio semplificato:

a(?!b(?!c)) 

a  Match: (?!b) succeeds 
ac  Match: (?!b) succeeds 
ab  No match: (?!b(?!c)) fails 
abe No match: (?!b(?!c)) fails 
abc Match: (?!b(?!c)) succeeds 

L'ultimo esempio è un doppia negazione: Permette un b seguito da c. Il lookahead negativo annidato diventa un aspetto positivo: dovrebbe essere presente lo c.

In ciascun esempio, solo lo a corrisponde. Il lookahead è solo una condizione e non si aggiunge al testo corrispondente.

+0

Se un lookahead negativo annidato ("double lookahead negativo") può diventare un aspetto positivo, è possibile indicare un equivalente in forma lookahead positiva? i.e: (a) Quale sarebbe la forma di lookahead positiva della mia doppia drupal lookahead negativo "'drupal-6.14/(?! sites (?!/all |/default)).' '" esempio? Sarebbe: 'drupal-6.14/(? = Siti/tutti | default). * ??? (b) Quale sarebbe la forma di lookahead positiva del tuo lookahead double negativo "(!? B (?! C))" esempio? – themesandmodules

+0

eww. scusa. la prima volta utilizzando i commenti qui che la formattazione è orribile. mal riformare modificando la domanda. – themesandmodules

+0

@willieseabrook: non pensateci, solo una parte del lookahead è doppiamente negativa, quindi non è possibile sostituire l'intero con uno positivo. – Andomar

12

Lookaround può essere nidificato.

Quindi questo regex "drupal-6.14 /" che è non seguito da "siti", che è non seguito da "/ all" o "/ default".

Confondere?Usando parole diverse, possiamo dire che corrisponda a "Drupal-6.14 /" cioè non seguito da "siti" a meno che è ulteriormente seguito da "/ all" o "/ default"

+0

Grazie per questo. E * sì * lo trovo ancora confondendo LOL. Penso che tu sia la citazione di "non seguito dai siti * a meno che * seguito da tutti | predefinito" sia abbastanza utile. – themesandmodules

1

Se si rivede l'espressione regolare in questo modo:

drupal-6.14/(?=sites(?!/all|/default)).* 
      ^^ 

... allora corrisponderà tutti gli input che contengono drupal-6.14/ seguito da sites seguita da qualcosa di diverso da/all o /default. Per esempio:

drupal-6.14/sites/foo 
drupal-6.14/sites/bar 
drupal-6.14/sitesfoo42 
drupal-6.14/sitesall 

Modifica ?= a ?! per abbinare il vostro regex originale semplicemente nega questi incontri:

drupal-6.14/(?!sites(?!/all|/default)).* 
      ^^ 

Quindi, questo significa semplicemente che drupal-6.14/ ora non può essere seguita da sites seguito da nulla diverso da/all o /default. Così ora, questi ingressi soddisferanno la regex:

drupal-6.14/sites/all 
drupal-6.14/sites/default 
drupal-6.14/sites/all42 

Ma, ciò che può non essere evidente da alcune delle altre risposte (e forse la tua domanda) è che il vostro regex consentirà anche altri ingressi dove drupal-6.14/ è seguito anche da altro oltre allo sites. Per esempio:

drupal-6.14/foo 
drupal-6.14/xsites 

Conclusione: Così, il vostro regex dice fondamentalmente per includere tutti i sottodirectory di drupal-6.14eccezione quelle che si trovano su sites il cui nome inizia con qualcosa di diverso all o default.