Comment faire une «correspondance inverse» avec regex?

112

J'utilise RegexBuddy mais j'ai quand même des problèmes avec cette chose: \

Je traite ligne par ligne un fichier. J'ai construit un "modèle de ligne" pour correspondre à ce que je veux.

Maintenant, je voudrais faire une correspondance inverse ... c'est-à-dire que je veux faire correspondre des lignes où il y a une chaîne de 6 lettres, mais seulement si ces six lettres ne sont pas Andrea , comment dois-je faire?


EDIT: J'écrirai le programme qui utilise cette regex, je ne sais pas encore si en python ou php, je fais ce truc d'abord pour apprendre quelques regex :) Il y a différents types de lignes, je voulais utiliser regex pour sélectionner le type qui m'intéresse. Une fois que j'ai ces lignes, je dois appliquer un autre filtre juste pour ne pas correspondre à une valeur connue, j'ai besoin de tous les autres, pas de ça. Le (?! Non-voulu) fonctionne plutôt bien, merci. :-)

J'espère que cela clarifie la question :)

Andrea Ambu
la source
Il semble en fait que vous feriez mieux de nous donner un peu plus d'informations sur ce que vous faites et de voir si quelqu'un peut proposer une solution alternative. En règle générale, tenter d'analyser un fichier entier en construisant une expression régulière qui correspond à chaque ligne est une route plutôt compliquée :)
Dan

Réponses:

70
(?!Andrea).{6}

En supposant que votre moteur d'expression régulière prend en charge les anticipations de recherche négatives.

Edit: ..ou peut-être préféreriez-vous utiliser [A-Za-z]{6}à la place de.{6}

Edit (encore): Notez que les lookaheads et lookbehinds ne sont généralement pas la bonne façon d '«inverser» une correspondance d'expression régulière. Les expressions régulières ne sont pas vraiment configurées pour faire des correspondances négatives, elles laissent cela dans la langue avec laquelle vous les utilisez.

Dan
la source
Vous devez ajouter le ^ que @Vinko Vrsalovic utilise pour qu'il ne corresponde pas à "ndrea \ n"
bdukes
2
. ne correspond pas \ n par défaut (certaines langues [par exemple Perl] vous permettent d'activer ce comportement, mais par défaut. correspond à tout MAIS \ n).
Dan
1
(de plus, l'OP n'a jamais mentionné que la chaîne devait se produire au début de la ligne)
Dan
1
que voulez-vous dire pour OP?
Andrea Ambu
1
Andrea: OP signifie «affiche originale», donc, je faisais référence à vous :)
Dan
47

Pour Python / Java,

^(.(?!(some text)))*$

http://www.lisnichenko.com/articles/javapython-inverse-regex.html

Dmytro
la source
4
Cela ne marche pas. Vous pensez à l'idiome Tempered Greedy Token. mais le point doit aller après la recherche, pas avant. Voir cette question . Mais cette approche est de toute façon excessive pour cette tâche.
Alan Moore
Je ne sais pas dans quelle langue il est écrit, mais a fonctionné comme un charme dans Sublime Text pour nettoyer mes données de test. Merci!
Matthias dirickx
1
@AlanMoore En fait, cela fonctionnera presque pour ce cas d'utilisation. Cependant, si some textcommence la ligne, cela renverra le mauvais résultat.
Zenexer
2
@Zenexer, c'est ce que je voulais dire. Si le point est après la recherche au lieu d'avant, cela fonctionne parfaitement.
Alan Moore
Voici un lien qui en explique plus. Je ne comprends pas pourquoi ?!et pas seulement !.
Timo
21

Mis à jour avec les commentaires d' Alan Moore

Dans PCRE et des variantes similaires, vous pouvez en fait créer une expression régulière qui correspond à toute ligne ne contenant pas de valeur:

^(?:(?!Andrea).)*$

C'est ce qu'on appelle un jeton gourmand tempéré . L'inconvénient est qu'il ne fonctionne pas bien.

Zenexer
la source
1
Il s'agit du jeton gourmand tempéré sous forme longue. Il suffit de mettre le point (ou [\s\S], ce qui est utile uniquement en JavaScript) après la deuxième préanalyse, et vous n'avez pas besoin le premier: ^(?:(?!Andrea).)*$.
Alan Moore
@AlanMoore Nice! Je n'ai trouvé aucun modèle établi qui fonctionnait comme ça, alors j'ai créé le mien. Plutôt que de prendre votre réponse, vous devriez la fournir comme la vôtre.
Zenexer
Ça va, il y a déjà plein de bonnes réponses. Et vous méritez le mérite d'avoir inventé l'idiome par vous-même. À votre santé!
Alan Moore
Pourquoi suggérez-vous d'utiliser [\S\s]? OP parle de lignes correspondantes, ne contenant pas le mot "Andrea". Il ne s'agit pas de vérifier si la chaîne entière contient ce mot. Est-ce que je manque quelque chose?
x-yuri
@ x-yuri je pense que vous avez raison. J'ai probablement répondu à la question que j'avais: j'ai visité cette page pour la première fois, en ignorant l'écart. Ma connexion n'est pas assez bonne pour mettre à jour la réponse maintenant, cependant (<10 kbps)
Zenexer
11

Quelle langue utilisez-vous? Les capacités et la syntaxe de l'implémentation de regex sont importantes pour cela.

Vous pouvez utiliser l'anticipation. Utiliser python comme exemple

import re

not_andrea = re.compile('(?!Andrea)\w{6}', re.IGNORECASE)

Pour décomposer cela:

(?! Andrea) signifie 'match si les 6 prochains caractères ne sont pas "Andrea"'; si oui alors

\ w signifie un "caractère de mot" - caractères alphanumériques. Ceci est équivalent à la classe [a-zA-Z0-9_]

\ w {6} signifie exactement 6 caractères de mot.

re.IGNORECASE signifie que vous allez exclure "Andrea", "andrea", "ANDREA" ...

Une autre façon est d'utiliser la logique de votre programme - utilisez toutes les lignes qui ne correspondent pas à Andrea et faites-les passer par une deuxième regex pour vérifier 6 caractères. Ou vérifiez d'abord au moins 6 caractères de mot, puis vérifiez qu'il ne correspond pas à Andrea.

Hamish Downer
la source
7

Assertion d'anticipation négative

(?!Andrea)

Ce n'est pas exactement une correspondance inversée, mais c'est le mieux que vous puissiez faire directement avec regex. Cependant, toutes les plates-formes ne les prennent pas en charge.

Vinko Vrsalovic
la source
1
Jusqu'à ce que l'interlocuteur clarifie, je ne vois pas que le match doit commencer au début de la ligne. Alors pourquoi le ^?
Hamish Downer
Parce que j'ai compris qu'il voulait vérifier au début de la ligne, édité compte tenu des clarifications
Vinko Vrsalovic
5

Si vous souhaitez faire cela dans RegexBuddy, il existe deux façons d'obtenir une liste de toutes les lignes ne correspondant pas à une expression régulière.

Dans la barre d'outils du panneau Test, définissez la portée du test sur "Ligne par ligne". Lorsque vous faites cela, un élément Liste toutes les lignes sans correspondance apparaîtra sous le bouton Tout répertorier dans la même barre d'outils. (Si vous ne voyez pas le bouton List All, cliquez sur le bouton Match dans la barre d'outils principale.)

Sur le panneau GREP, vous pouvez activer les cases à cocher «basé sur les lignes» et «inverser les résultats» pour obtenir une liste des lignes non correspondantes dans les fichiers que vous parcourez.

Jan Goyvaerts
la source
5

(?!est utile dans la pratique. Bien qu'à proprement parler, regarder vers l'avenir n'est pas une expression régulière telle que définie mathématiquement.

Vous pouvez écrire une expression régulière inversée manuellement.

Voici un programme pour calculer le résultat automatiquement. Son résultat est généré par la machine, qui est généralement beaucoup plus complexe que l'écriture manuelle. Mais le résultat fonctionne.

faible
la source
1

Je viens de proposer cette méthode qui peut être intensive en matériel mais qui fonctionne:

Vous pouvez remplacer tous les caractères qui correspondent à l'expression régulière par une chaîne vide.

Ceci est un oneliner:

notMatched = re.sub(regex, "", string)

J'ai utilisé cela parce que j'étais obligé d'utiliser une expression régulière très complexe et que je ne pouvais pas comprendre comment inverser chaque partie de celle-ci dans un laps de temps raisonnable.

Cela ne vous renverra que le résultat de la chaîne, pas les objets correspondants!

Matthias Herrmann
la source
-3

En perl, vous pouvez faire

process ($ line) if ($ line = ~! / Andrea /);

phreakre
la source
4
Cette syntaxe est fausse. Je pense que vous voulez dire processus ($ line) si $ line! ~ / Andrea /
dland