web-dev-qa-db-fra.com

Vim Regex: Comment rechercher A ET B PAS C

J'ai plusieurs lignes contenant les noms des présidents américains Carter, Bush, Clinton, Obama. Certains contiennent 1 de ces noms, certains 2, certains 3, certains tous les 4 (dans n'importe quel ordre).

Je sais comment rechercher Carter ET Clinton ET Obama ->

:g/.*Carter\&.*Clinton\&.*Obama/p

Je sais comment rechercher Carter AND (Clinton OR Bush) ->

:g/.*Carter\&\(.*Clinton\|.*Bush\)/p

(Il existe certainement de meilleures façons de le faire)

Mais je ne sais pas comment chercher (et j'ai regardé les questions connexes), par exemple, pour Bush ET Clinton PAS Carter et encore moins comment rechercher, par exemple, pour Bush ET Clinton PAS (Carter OR Obama).

49
ThG

Pour représenter un NON, utilisez l'assertion négative \@! .

Par exemple, "PAS Bush" serait:

^\(.*Bush\)\@!

ou en utilisant \v :

\v^(.*Bush)@!

Important: notez le premier ^. Bien qu'il soit facultatif si vous n'utilisez que des assertions positives (une correspondance est aussi bonne qu'une autre), il est nécessaire d'ancrer les assertions négatives (sinon elles peuvent toujours correspondre à la fin d'une ligne).

Traduction de "Bush ET Clinton ET PAS (Carter OR Obama)":

\v^(.*Bush)&(.*Clinton)&(.*Carter|.*Obama)@!

Addenda

Pour expliquer la relation entre \& et \@= :

One&Two&Three

est interchangeable avec:

(One)@=(Two)@=Three

La seule différence est que \& reflète directement \| (qui devrait être plus évident et naturel), tandis que \@= reflète Perl's (?=pattern) .

51
Pi Delport

Si vous voulez utiliser des expressions régulières de style Perl après vim, oubliez \&: C'est une fonctionnalité spécifique à vim qui est inutile car vim a aussi des lookaheads, donc tout r1\&r2 Peut être réécrit comme comme \%(r1\)\@=r2. Mais les lookaheads sont meilleurs car il existe une version négative et ils sont également disponibles dans la plupart des moteurs d'expression régulière de style Perl. Votre (Bush AND Clinton AND NOT (Carter OR Obama)) Peut s'exprimer de la manière suivante:

g/^\%(.*\%(Carter\|Obama\)\)\@!\%(.*Bush\)\@=.*Clinton/

Ou, avec beaucoup de magie:

g/^\v%(.*%(Carter|Obama))@!%(.*Bush)@=.*Clinton/

Voir :h /\@=

À propos de la logique interne: l'anticipation est comme des branches: pour regex (reg1)@=reg2 En supposant que reg2 Correspond à la position N (la correspondance commence à la position N), regex le moteur vérifie si reg1 correspond également à cette position. Si ce n'est pas le cas, la position est supprimée et le moteur d'expression régulière tente la prochaine correspondance possible pour reg2. Idem pour l'anticipation négative, mais avec la différence que le moteur d'expression régulière rejette la position si reg1correspond correspond.


Exemple:

Regex: (.b)@!a.

Chaîne: aba.

  1. Correspondance trouvée: a correspond à la position 0 (aba). Essayer de faire correspondre l'anticipation: . Correspond à a (aba) Et b correspond à b (aba) , matchs à venir, position de défausse.
  2. La position 1 (aba) Ne correspond pas à a.
  3. Correspondance trouvée: a correspond à la position 2 (aba). Essayer de faire correspondre l'anticipation: . Correspond à a (aba), Mais b ne correspond pas: aucun symbole n'est laissé, l'anticipation échoue. Résultat: l'expression régulière correspond à la position 2.
14
ZyX