web-dev-qa-db-fra.com

Anticipation négative d'expression régulière

Dans mon répertoire personnel, j'ai un dossier drupal-6.14 qui contient la plate-forme 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

Ce que cette commande fait est gzips le dossier drupal-6.14, excluant tous les sous-dossiers de drupal-6.14/sites / sauf sites/all et sites/default , qu'il 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 veux exclure, mais je ne comprends pas très bien pourquoi.

Il s'agit d'une tâche courante utilisant des expressions régulières pour

Correspond à toutes les chaînes, sauf celles qui ne contiennent pas le sous-modèle x. Ou en d'autres termes, nier un sous-modèle.

Je (pense) que je comprends que la stratégie générale pour résoudre ces problèmes est l'utilisation d'anticipations négatives, mais je n'ai jamais compris à un niveau satisfaisant le fonctionnement des regards positifs et négatifs (avant/arrière).

Au fil des ans, j'ai lu de nombreux sites Web dessus. Les manuels PHP et Python regex, d'autres pages comme http://www.regular-expressions.info/lookaround.html et ainsi de suite, mais je n'ai jamais vraiment vraiment compris cela.

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

- Mise à jour un:

En ce qui concerne la réponse d'Andomar: une double anticipation négative peut-elle être exprimée plus succinctement en une seule affirmation positive:

c'est-à-dire:

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

équivalent à:

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

???

- Deuxième mise à jour:

Selon @andomar et @alan moore - vous ne pouvez pas échanger la double recherche négative pour la recherche positive.

55
themesandmodules

Une anticipation négative indique qu'à cette position, l'expression régulière suivante ne peut pas correspondre.

Prenons un exemple simplifié:

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

Le dernier exemple est un double négation: il permet un b suivi de c. La tête de lecture négative imbriquée devient une tête de lecture positive: le c doit être présent.

Dans chaque exemple, seul le a est mis en correspondance. Le lookahead n'est qu'une condition et n'ajoute rien au texte correspondant.

103
Andomar

Les contournements peuvent être imbriqués.

Donc, cette expression régulière correspond à "drupal-6.14 /" qui est pas suivi de "sites" qui est pas suivi de "/ all" ou "/ default".

Déroutant? En utilisant des mots différents, nous pouvons dire qu'il correspond à "drupal-6.14 /" qui est pas suivi de "sites" à moins que qui soit suivi par "/ all" ou "/ défaut"

12
ʞɔıu

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

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

... alors il correspondra à toutes les entrées qui contiennent drupal-6.14/ suivi de sites suivi de autre chose que /all ou /default. Par exemple:

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

En changeant ?= à ?! pour correspondre à votre regex d'origine annule simplement ces correspondances:

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

Donc, cela signifie simplement que drupal-6.14/ maintenant ne peut pas être suivi par sites suivi par autre chose que /all ou /default. Alors maintenant, ces entrées satisferont l'expression régulière:

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

Mais, ce qui peut ne pas être évident à partir de certaines des autres réponses (et peut-être de votre question), c'est que votre expression régulière permettra également d'autres entrées drupal-6.14/ est également suivi par autre chose que sites. Par exemple:

drupal-6.14/foo
drupal-6.14/xsites

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

2
DavidRR