116 votes

Expression régulière de négation anticipée

Dans mon répertoire personnel, j'ai un dossier drupal-6.14 qui contient la plateforme Drupal.

À partir de ce répertoire, j'utilise la commande suivante :

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

Cette commande gzipe le dossier drupal-6.14, en excluant tous les sous-dossiers de drupal-6.14/sites/ sauf sites/all et sites/default, qu'elle inclut.

Ma question porte sur l'expression régulière :

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

L'expression fonctionne pour exclure tous les dossiers que je souhaite exclure, mais je n'ai pas tout à fait compris pourquoi.

C'est une tâche courante d'utiliser des expressions régulières pour

Faire correspondre toutes les chaînes, à l'exception de celles qui ne contiennent pas le sous-modèle x. Ou en d'autres termes, nier un sous-modèle.

Je (pense) comprendre que la stratégie générale pour résoudre ces problèmes est l'utilisation de lookaheads négatifs, mais je n'ai jamais bien compris à quel niveau les look(ahead/behind)s positifs et négatifs fonctionnent.

Au fil des ans, j'ai lu de nombreux sites Web à leur sujet. Les manuels de regex PHP et Python, d'autres pages comme http://www.regular-expressions.info/lookaround.html et ainsi de suite, mais je n'ai jamais vraiment eu une compréhension solide d'eux.

Quelqu'un pourrait-il expliquer comment cela fonctionne, et peut-être fournir quelques exemples similaires qui feraient des choses similaires ?

-- Mise à jour Un :

En ce qui concerne la réponse d'Andomar : peut-on exprimer plus succinctement un double negative lookahead comme une seule instruction positive lookahead :

c'est-à-dire :

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

équivalent à :

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

???

-- Mise à jour Deux :

Comme l'ont souligné @andomar et @alan moore - on ne peut pas remplacer un double negative lookahead par un positive lookahead.

203voto

Andomar Points 115404

Un regard en arrière négatif indique que, à cette position, l'expression régulière suivante ne peut pas correspondre.

Prenons un exemple simplifié:

a(?!b(?!c))

a      Correspondance: (?!b) est réussi
ac     Correspondance: (?!b) est réussi
ab     Aucune correspondance: (?!b(?!c)) échoue
abe    Aucune correspondance: (?!b(?!c)) échoue
abc    Correspondance: (?!b(?!c)) est réussi

Le dernier exemple est une double négation : elle permet b suivi de c. Le regard en arrière négatif imbriqué devient un regard en avant positif : le c doit être présent.

Dans chaque exemple, seule la lettre a est correspondante. Le regard en arrière négatif est uniquement une condition, et n'ajoute pas au texte correspondant.

15voto

ʞɔıu Points 15907

Les lookarounds peuvent être imbriqués.

Alors cette regex correspond à "drupal-6.14/" qui n'est pas suivi par "sites" qui n'est pas suivi par "/all" ou "/default".

Confus ? En utilisant des mots différents, nous pouvons dire que cela correspond à "drupal-6.14/" qui n'est pas suivi par "sites" à moins que cela soit ensuite suivi par "/all" ou "/default"

9voto

DavidRR Points 2211

Si vous modifiez votre expression régulière comme ceci :

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

...alors elle correspondra à toutes les entrées qui contiennent drupal-6.14/ suivi de sites suivi de n'importe quoi sauf /all ou /default. Par exemple :

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

Changer ?= en ?! pour correspondre à votre regex original négative simplement annule ces correspondances :

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

Cela signifie simplement que drupal-6.14/ ne peut désormais pas être suivi de sites suivi de n'importe quoi sauf /all ou /default. Ainsi, ces entrées satisferont maintenant la regex :

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

Mais, ce qui peut ne pas être évident dans certaines autres réponses (et éventuellement dans votre question) est que votre regex permettra également d'autres entrées où drupal-6.14/ est suivi de n'importe quelle chose sauf sites aussi. Par exemple :

drupal-6.14/foo
drupal-6.14/xsites

Conclusion : Ainsi, votre regex dit essentiellement d'inclure tous les sous-répertoires de drupal-6.14 sauf ceux des sous-répertoires de sites dont le nom commence par autre chose que all ou default.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X