Expression régulière pour correspondre à une ligne qui ne contient pas de mot


4295

Je sais qu'il est possible de faire correspondre un mot, puis d'inverser les correspondances à l'aide d'autres outils (par exemple grep -v). Cependant, est-il possible de faire correspondre des lignes qui ne contiennent pas de mot spécifique, par exemple hedeen utilisant une expression régulière?

Contribution:

hoho
hihi
haha
hede

Code:

grep "<Regex for 'doesn't contain hede'>" input

Sortie désirée:

hoho
hihi
haha

85
Probablement quelques années de retard, mais quel est le problème avec ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*:? L'idée est simple. Gardez la correspondance jusqu'à ce que vous voyiez le début de la chaîne indésirable, puis ne correspondez que dans les N-1 cas où la chaîne n'est pas terminée (où N est la longueur de la chaîne). Ces cas N-1 sont "h suivi de non-e", "il a suivi de non-d" et "hed suivi de non-e". Si vous avez réussi à passer ces N-1 cas, avec succès ne pas correspondre à la chaîne non désirée afin que vous puissiez commencer à chercher à [^h]*nouveau
stevendesu

323
@stevendesu: essayez ceci pour «un mot très très très long» ou encore mieux une demi-phrase. Amusez-vous à taper. BTW, c'est presque illisible. Je ne connais pas l'impact sur les performances.
Peter Schuetze

13
@PeterSchuetze: Bien sûr, ce n'est pas joli pour des mots très très longs, mais c'est une solution viable et correcte. Bien que je n'ai pas effectué de tests sur les performances, je n'imagine pas que ce soit trop lent car la plupart de ces dernières règles sont ignorées jusqu'à ce que vous voyiez un h (ou la première lettre du mot, de la phrase, etc.). Et vous pouvez facilement générer la chaîne d'expression régulière pour les chaînes longues en utilisant la concaténation itérative. Si cela fonctionne et peut être généré rapidement, la lisibilité est-elle importante? C'est à cela que servent les commentaires.
stevendesu

57
@stevendesu: je suis encore plus tard, mais cette réponse est presque complètement fausse. d'une part, il exige que le sujet contienne "h" qu'il ne devrait pas avoir, étant donné que la tâche est "des lignes de correspondance qui [ne] contiennent pas de mot spécifique". supposons que vous vouliez rendre le groupe interne facultatif et que le modèle est ancré: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ cela échoue lorsque les instances de "hede" sont précédées par des instances partielles de "hede" comme dans "hhede".
jaytea

8
Cette question a été ajoutée à la FAQ sur l'expression régulière de débordement de pile , sous "Regex-Fu avancé".
aliteralmind

Réponses:


5897

L'idée que l'expression régulière ne prend pas en charge la correspondance inverse n'est pas entièrement vraie. Vous pouvez imiter ce comportement à l'aide de contournements négatifs:

^((?!hede).)*$

L'expression régulière ci-dessus correspondra à n'importe quelle chaîne ou ligne sans saut de ligne, ne contenant pas la (sous) chaîne 'hede'. Comme mentionné précédemment, ce n'est pas quelque chose regex est « bon » à (ou devrait faire), mais encore, il est possible.

Et si vous devez également faire correspondre les caractères de saut de ligne, utilisez le modificateur DOT-ALL (la fin sdans le modèle suivant):

/^((?!hede).)*$/s

ou utilisez-le en ligne:

/(?s)^((?!hede).)*$/

(où /.../sont les délimiteurs d'expressions régulières, c'est-à-dire ne faisant pas partie du modèle)

Si le modificateur DOT-ALL n'est pas disponible, vous pouvez imiter le même comportement avec la classe de caractères [\s\S]:

/^((?!hede)[\s\S])*$/

Explication

Une chaîne n'est qu'une liste de ncaractères. Avant et après chaque caractère, il y a une chaîne vide. Ainsi, une liste de ncaractères aura n+1des chaînes vides. Considérez la chaîne "ABhedeCD":

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = e1 A e2 B e3 h e4 e e5 d e6 e e7 C e8 D e9
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

où les esont les chaînes vides. Le regex (?!hede).regarde en avant pour voir s'il n'y a pas de sous-chaîne "hede"à voir, et si c'est le cas (donc quelque chose d'autre est vu), alors le .(point) correspondra à n'importe quel caractère sauf un saut de ligne. Les ressemblances sont également appelées assertions de largeur nulle car elles ne consomment aucun caractère. Ils ne font qu'affirmer / valider quelque chose.

Ainsi, dans mon exemple, chaque chaîne vide est d'abord validée pour voir s'il n'y en a pas "hede", avant qu'un caractère ne soit consommé par le .(point). Le regex (?!hede).fera qu'une seule fois, il est enveloppé dans un groupe, et répété zéro ou plus: ((?!hede).)*. Enfin, le début et la fin de l'entrée sont ancrés pour s'assurer que l'entrée entière est consommée:^((?!hede).)*$

Comme vous pouvez le voir, l'entrée "ABhedeCD"échouera car e3, le regex (?!hede)échoue (il y a "hede" de l'avance!).


26
Je n'irais pas jusqu'à dire que c'est quelque chose de mauvais. La commodité de cette solution est assez évidente et les performances atteintes par rapport à une recherche programmatique vont souvent être sans importance.
Archimaredes

29
Le loook-ahead négatif à proprement parler vous rend l'expression régulière non régulière.
Peter K

55
@PeterK, bien sûr, mais c'est SO, pas MathOverflow ou CS-Stackexchange. Les personnes qui posent une question ici recherchent généralement une réponse pratique. La plupart des bibliothèques ou des outils (comme ceux grepque l'OP mentionne) avec support regex ont tous des fonctionnalités qui les rendent non réguliers au sens théorique.
Bart Kiers

19
@Bart Kiers, ne vous offensez pas, juste cet abus de terminologie m'irrite un peu. La partie vraiment déroutante ici est que les expressions régulières au sens strict peuvent très bien faire ce que OP veut, mais le langage commun pour les écrire ne le permet pas, ce qui conduit à des solutions de contournement (mathématiquement laides) comme des anticipations. Veuillez voir cette réponse ci-dessous et mon commentaire là-bas pour (correctement aligné théoriquement) la façon de le faire. Inutile de dire que cela fonctionne plus rapidement sur les grandes entrées.
Peter K

17
Au cas où vous vous seriez jamais demandé comment faire cela dans vim:^\(\(hede\)\@!.\)*$
baldrs

739

Notez que la solution de ne commence pas par «hede» :

^(?!hede).*$

est généralement beaucoup plus efficace que la solution ne contient pas de "hede" :

^((?!hede).)*$

Le premier vérifie «hede» uniquement à la première position de la chaîne d'entrée, plutôt qu'à chaque position.


5
Merci, je l'ai utilisé pour valider que la chaîne ne contient pas de séquence de chiffres ^ ((?? \ D {5,}).) *
Samih A

2
salut! Je ne peux pas composer ne se termine pas par l'expression régulière "hede" . Pouvez-vous m'aider?
Aleks Ya

1
@AleksYa: utilisez simplement la version "contient" et incluez l'ancre de fin dans la chaîne de recherche: changez la chaîne en "ne correspond pas" de "hede" à "hede $"
Nyerguds

2
@AleksYa: la ne termine pas la version peut être effectuée à l' aide de lookbehind négatif: (.*)(?<!hede)$. La version de @Nyerguds fonctionnerait également, mais manque complètement le point sur les performances mentionné dans la réponse.
thisismydesign

5
Pourquoi tant de réponses disent-elles ^((?!hede).)*$? N'est-ce pas plus efficace à utiliser ^(?!.*hede).*$? Il fait la même chose mais en moins d'étapes
JackPRead

208

Si vous ne l'utilisez que pour grep, vous pouvez utiliser grep -v hedepour obtenir toutes les lignes qui ne contiennent pas de hede.

ETA Oh, en relisant la question, grep -vc'est probablement ce que vous vouliez dire par "options d'outils".


22
Astuce: pour filtrer progressivement ce que vous ne voulez pas: grep -v "hede" | grep -v "hihi" | ...etc.
Olivier Lalonde

51
Ou en utilisant un seul processusgrep -v -e hede -e hihi -e ...
Olaf Dietsche

15
Ou tout simplement grep -v "hede\|hihi":)
Putnik

2
Si vous souhaitez filtrer de nombreux modèles, placez-les dans un fichier et utilisezgrep -vf pattern_file file
codeforester

4
Ou tout simplement egrepou grep -Ev "hede|hihi|etc"pour éviter la fuite maladroite.
Amit Naidu

161

Réponse:

^((?!hede).)*$

Explication:

^le début de la chaîne, le (groupe et la capture à \ 1 (0 fois ou plus (correspondant au plus grand nombre possible)),
(?!regardez en avant pour voir s'il n'y en a pas,

hede votre chaîne,

)fin d'anticipation, .tout caractère sauf \ n,
)*fin de \ 1 (Remarque: comme vous utilisez un quantificateur sur cette capture, seule la DERNIÈRE répétition du motif capturé sera stockée dans \ 1)
$avant un \ n facultatif, et la fin de la chaîne


14
génial qui a fonctionné pour moi dans le texte sublime 2 en utilisant plusieurs mots ' ^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'
Damodar Bashyal

3
@DamodarBashyal Je sais que je suis assez en retard ici, mais vous pourriez totalement supprimer le deuxième mandat là-bas et vous obtiendriez exactement les mêmes résultats
forresthopkinsa

99

Les réponses données sont parfaitement bien, juste un point académique:

Les expressions régulières au sens de l'informatique théorique NE SONT PAS CAPABLES de le faire comme ça. Pour eux, cela devait ressembler à ceci:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

Cela ne fait qu'une correspondance COMPLÈTE. Le faire pour les sous-matchs serait encore plus gênant.


1
Il est important de noter que cela n'utilise que les expressions régulières POSIX.2 de base et donc, tandis que le terse est plus portable lorsque PCRE n'est pas disponible.
Steve-o

5
Je suis d'accord. De nombreuses expressions régulières, sinon la plupart, ne sont pas des langages réguliers et ne peuvent pas être reconnues par un automate fini.
ThomasMcLeod

@ThomasMcLeod, Hades32: Est-ce dans les domaines de tout langage régulier possible de pouvoir dire " non " et " et " ainsi que le " ou " d'une expression telle que " (hede|Hihi)"? (C'est peut-être une question pour CS.)
James Haigh

7
@JohnAllen: MOI !!! … Eh bien, pas le regex réel, mais la référence académique, qui est également étroitement liée à la complexité informatique; Les PCRE ne peuvent fondamentalement pas garantir la même efficacité que les expressions régulières POSIX.
James Haigh

4
Désolé - cette réponse ne fonctionne tout simplement pas, elle correspondra à Hhehe et même à Hehe partiellement (la seconde moitié)
Falco

60

Si vous souhaitez que le test d'expression rationnelle échoue uniquement si la chaîne entière correspond, les éléments suivants fonctionneront:

^(?!hede$).*

par exemple - Si vous souhaitez autoriser toutes les valeurs sauf "foo" (c'est-à-dire "foofoo", "barfoo" et "foobar" passeront, mais "foo" échouera), utilisez: ^(?!foo$).*

Bien sûr, si vous vérifiez l' égalité exacte , une meilleure solution générale dans ce cas est de vérifier l'égalité des chaînes, c'est-à-dire

myStr !== 'foo'

Vous pouvez même mettre la négation en dehors du test si vous avez besoin de fonctionnalités d'expression régulière (ici, insensibilité à la casse et correspondance de plage):

!/^[a-f]oo$/i.test(myStr)

La solution de regex en haut de cette réponse peut être utile, cependant, dans les situations où un test de regex positif est requis (peut-être par une API).


qu'en est-il des espaces vides? Par exemple, si je veux que le test échoue avec une chaîne " hede "?
2017

@eagor la \sdirective correspond à un seul caractère d'espace blanc
Roy Tinker

merci, mais je n'ai pas réussi à mettre à jour l'expression régulière pour que cela fonctionne.
eagor

2
@eagor:^(?!\s*hede\s*$).*
Roy Tinker

52

FWIW, puisque les langages réguliers (aka langages rationnels) sont fermés par complémentation, il est toujours possible de trouver une expression régulière (aka expression rationnelle) qui nie une autre expression. Mais peu d'outils implémentent cela.

Vcsn prend en charge cet opérateur (qu'il désigne par le {c}suffixe).

Vous définissez d' abord le type de vos expressions: les étiquettes sont lettre ( lal_char) à choisir aà zpar exemple (définir l'alphabet lorsque vous travaillez avec complémentation est, bien sûr, très important), et la « valeur » calculée pour chaque mot est juste un booléen : truele mot est accepté false, rejeté.

En Python:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}  𝔹

puis vous entrez votre expression:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

convertissez cette expression en automate:

In [7]: a = e.automaton(); a

L'automate correspondant

enfin, reconvertissez cet automate en une expression simple.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

+est généralement indiqué |, \edésigne le mot vide et [^]est généralement écrit .(n'importe quel caractère). Donc, avec un peu de réécriture ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*.

Vous pouvez voir cet exemple ici et essayer Vcsn en ligne là-bas .


6
Vrai, mais moche, et uniquement faisable pour les petits jeux de caractères. Vous ne voulez pas faire cela avec des chaînes Unicode :-)
reinierpost

Il existe plus d'outils qui le permettent, l'un des plus impressionnants étant Ragel . Là, il serait écrit comme (tout * - ('hehe' tout *)) pour une correspondance alignée au début ou (tout * - ('hehe' tout *)) pour non aligné.
Peter K

1
@reinierpost: pourquoi est-ce moche et quel est le problème avec unicode? Je ne peux pas être d'accord sur les deux. (Je n'ai aucune expérience avec vcsn, mais avec DFA).
Peter K

3
@PedroGimeno Lorsque vous avez ancré, vous vous êtes assuré de mettre cette expression régulière en parens en premier? Sinon, les précédences entre les ancres et |ne joueront pas bien. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'.
akim

1
Je pense qu'il vaut la peine de remarquer que cette méthode est pour faire correspondre les lignes qui ne sont pas le mot «hede», plutôt que les lignes qui ne contiennent pas le mot «hede» qui est ce que l'OP a demandé. Voir ma réponse pour ce dernier.
Pedro Gimeno

51

Voici une bonne explication de la raison pour laquelle il n'est pas facile de nier une expression rationnelle arbitraire. Je dois cependant être d'accord avec les autres réponses: s'il ne s'agit pas d'une question hypothétique, alors l'expression rationnelle n'est pas le bon choix ici.


10
Certains outils, et en particulier mysqldumpslow, n'offrent que cette façon de filtrer les données, donc dans un tel cas, trouver une regex pour le faire est la meilleure solution en dehors de la réécriture de l'outil (divers correctifs pour cela n'ont pas été inclus par MySQL AB / Sun / Oracle.
FGM

1
Exactement analogue à ma situation. Le moteur de modèle Velocity utilise des expressions régulières pour décider quand appliquer une transformation (échappement html) et je veux qu'il fonctionne toujours SAUF dans une situation.
Henno Vermeulen

1
Quelle alternative existe-t-il? Je n'ai jamais rencontré quoi que ce soit qui puisse faire une correspondance de chaîne précise en plus de l'expression régulière. Si OP utilise un langage de programmation, il peut y avoir d'autres outils disponibles, mais s'il n'utilise pas d'écriture de code, il n'y a probablement pas d'autre choix.
kingfrito_5005

2
L'un des nombreux scénarios non hypothétiques où une expression régulière est le meilleur choix disponible: je suis dans un IDE (Android Studio) qui affiche la sortie du journal, et les seuls outils de filtrage fournis sont: les chaînes simples et l'expression régulière. Essayer de le faire avec des chaînes simples serait un échec complet.
LarsH

48

Avec l'anticipation négative, l'expression régulière peut correspondre à quelque chose qui ne contient pas de motif spécifique. Ceci est répondu et expliqué par Bart Kiers. Grande explication!

Cependant, avec la réponse de Bart Kiers, la partie d'anticipation testera 1 à 4 caractères à l'avance tout en faisant correspondre n'importe quel caractère. Nous pouvons éviter cela et laisser la partie d'anticipation vérifier tout le texte, s'assurer qu'il n'y a pas de «haie», puis la partie normale (. *) Peut manger tout le texte en une seule fois.

Voici l'expression rationnelle améliorée:

/^(?!.*?hede).*$/

Notez que le quantificateur paresseux (*?) Dans la partie d'anticipation négative est facultatif, vous pouvez utiliser le quantificateur gourmand (*) à la place, en fonction de vos données: si 'hede' est présent et dans la moitié du début du texte, le quantificateur paresseux peut Être plus rapide; sinon, le quantificateur gourmand sera plus rapide. Cependant, si «hede» n'est pas présent, les deux seraient également lents.

Voici le code de démonstration .

Pour plus d'informations sur Lookahead, veuillez consulter le grand article: Maîtriser Lookahead et Lookbehind .

Consultez également RegexGen.js , un générateur d'expressions régulières JavaScript qui aide à construire des expressions régulières complexes. Avec RegexGen.js, vous pouvez construire le regex d'une manière plus lisible:

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);

3
afin de simplement vérifier si la chaîne donnée ne contient pas str1 et str2:^(?!.*(str1|str2)).*$
S.Serpooshan

1
Oui, ou vous pouvez utiliser un quantificateur paresseux:, ^(?!.*?(?:str1|str2)).*$selon vos données. Ajout du ?:car nous n'avons pas besoin de le capturer.
amobiz

C'est de loin la meilleure réponse d'un facteur de 10xms. Si vous avez ajouté votre code jsfiddle et les résultats à la réponse, les gens pourraient le remarquer. Je me demande pourquoi la version paresseuse est plus rapide que la version gourmande quand il n'y a pas de hede. Ne devraient-ils pas prendre autant de temps?
user5389726598465

Oui, ils prennent le même temps car ils testent tous les deux le texte entier.
amobiz

41

Repères

J'ai décidé d'évaluer certaines des options présentées et de comparer leurs performances, ainsi que d'utiliser de nouvelles fonctionnalités. Analyse comparative sur .NET Regex Engine: http://regexhero.net/tester/

Texte de référence:

Les 7 premières lignes ne doivent pas correspondre, car elles contiennent l'expression recherchée, tandis que les 7 lignes inférieures doivent correspondre!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

Résultats:

Les résultats sont des itérations par seconde comme la médiane de 3 courses - Plus grand nombre = meilleur

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

Étant donné que .NET ne prend pas en charge les verbes d'action (* FAIL, etc.), je n'ai pas pu tester les solutions P1 et P2.

Sommaire:

J'ai essayé de tester la plupart des solutions proposées, certaines optimisations sont possibles pour certains mots. Par exemple, si les deux premières lettres de la chaîne de recherche ne sont pas identiques, la réponse 03 peut être développée pour ^(?>[^R]+|R+(?!egex Hero))*$entraîner un petit gain de performances.

Mais la solution globale la plus lisible et la plus rapide en termes de performances semble être 05 en utilisant une instruction conditionnelle ou 04 avec le quantificateur possessif. Je pense que les solutions Perl devraient être encore plus rapides et plus lisibles.


5
Vous devriez ^(?!.*hede)aussi chronométrer . /// En outre, il est probablement préférable de classer séparément les expressions du corpus correspondant et du corpus non correspondant, car c'est généralement le cas que la plupart des lignes correspondent ou que la plupart des lignes ne le font pas.
ikegami

32

Pas regex, mais je l'ai trouvé logique et utile d'utiliser des greps série avec un tuyau pour éliminer le bruit.

par exemple. rechercher un fichier de configuration apache sans tous les commentaires-

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

et

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

La logique du grep série est (pas un commentaire) et (correspond à dir)


2
Je pense qu'il demande la version regex de lagrep -v
Angel.King.47

9
C'est dangereux. Manque également des lignes commegood_stuff #comment_stuff
Xavi Montero

29

avec cela, vous évitez de tester une anticipation sur chaque position:

/^(?:[^h]+|h++(?!ede))*+$/

équivalent à (pour .net):

^(?>(?:[^h]+|h+(?!ede))*)$

Ancienne réponse:

/^(?>[^h]+|h+(?!ede))*$/

7
Bon point; Je suis surpris que personne n'ait mentionné cette approche auparavant. Cependant, cette expression régulière particulière est sujette à un retour en arrière catastrophique lorsqu'elle est appliquée à du texte qui ne correspond pas. Voici comment je le ferais:/^[^h]*(?:h+(?!ede)[^h]*)*$/
Alan Moore

... ou vous pouvez simplement rendre tous les quantificateurs possessifs. ;)
Alan Moore

@Alan Moore - Je suis aussi surpris. J'ai vu votre commentaire (et le meilleur regex dans la pile) ici seulement après avoir publié ce même modèle dans une réponse ci-dessous.
ridgerunner

@ridgerunner, ne doit pas nécessairement être le meilleur. J'ai vu des repères où la meilleure réponse fonctionne mieux. (J'ai été surpris à ce sujet.)
Qtax

23

Ce qui précède (?:(?!hede).)*est génial car il peut être ancré.

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

Mais ce qui suit suffirait dans ce cas:

^(?!.*hede)                    # A line without hede

Cette simplification est prête à ajouter des clauses "ET":

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

20

Voici comment je le ferais:

^[^h]*(h(?!ede)[^h]*)*$

Précis et plus efficace que les autres réponses. Il met en œuvre la technique d'efficacité "déroulant la boucle" de Friedl et nécessite beaucoup moins de retour en arrière.


17

Si vous voulez faire correspondre un caractère pour nier un mot similaire à nier la classe de caractères:

Par exemple, une chaîne:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

Ne pas utiliser:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

Utilisation:

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

L'avis "(?!bbb)."n'est ni lookbehind ni lookahead, c'est lookcurrent, par exemple:

"(?=abc)abcde", "(?!abc)abcde"

3
Il n'y a pas de "lookcurrent" dans les expressions rationnelles de perl. C'est vraiment un lookahead négatif (préfixe (?!). Le préfixe lookahead positif serait alors (?=que les préfixes lookbehind correspondants seraient (?<!et (?<=respectivement. Une anticipation signifie que vous lisez les caractères suivants (donc «en avance») sans les consommer. Un lookbehind signifie que vous vérifiez les personnages qui ont déjà été consommés.
Didier L

14

Une variante, à mon avis, plus lisible de la réponse du haut:

^(?!.*hede)

Fondamentalement, "correspond au début de la ligne si et seulement si elle ne contient pas" hede "" - donc l'exigence s'est traduite presque directement en expression régulière.

Bien sûr, il est possible d'avoir plusieurs exigences de défaillance:

^(?!.*(hede|hodo|hada))

Détails: l' ancre ^ garantit que le moteur d'expression régulière ne réessaye pas la correspondance à chaque emplacement de la chaîne, ce qui correspondrait à chaque chaîne.

L'ancre ^ au début est censée représenter le début de la ligne. L'outil grep correspond à chaque ligne une par une, dans les contextes où vous travaillez avec une chaîne multiligne, vous pouvez utiliser l'indicateur "m":

/^(?!.*hede)/m # JavaScript syntax

ou

(?m)^(?!.*hede) # Inline flag

Excellent exemple avec négation multiple.
Peter Parada

Une différence par rapport à la réponse du haut est que cela ne correspond à rien, et cela correspond à toute la ligne si sans "hede"
Z. Khullah

13

L'OP n'a pas précisé ni Tagle post pour indiquer le contexte (langage de programmation, éditeur, outil) dans lequel Regex sera utilisé.

Pour moi, je dois parfois le faire lors de l'édition d'un fichier à l'aide de Textpad.

Textpad prend en charge certains Regex, mais ne prend pas en charge lookahead ou lookbehind, donc cela prend quelques étapes.

Si je cherche à conserver toutes les lignes qui NE contiennent PAS la chaîne hede, je le ferais comme ceci:

1. Recherchez / remplacez le fichier entier pour ajouter un "Tag" unique au début de chaque ligne contenant n'importe quel texte.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Supprimez toutes les lignes contenant la chaîne hede( la chaîne de remplacement est vide):

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3. À ce stade, toutes les lignes restantes NE contiennent PAS la chaîne hede. Supprimez le "Tag" unique de toutes les lignes (la chaîne de remplacement est vide):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

Vous avez maintenant le texte d'origine avec toutes les lignes contenant la chaîne hedesupprimées.


Si je cherche à faire quelque chose d'autre uniquement aux lignes qui ne contiennent pas la chaîne hede, je le ferais comme ceci:

1. Recherchez / remplacez le fichier entier pour ajouter un "Tag" unique au début de chaque ligne contenant n'importe quel texte.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Pour toutes les lignes contenant la chaîne hede, supprimez le "Tag" unique:

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3. À ce stade, toutes les lignes qui commencent par l'unique "Tag", NE contiennent PAS la chaîne hede. Je peux maintenant faire mon autre chose uniquement pour ces lignes.

4. Lorsque j'ai terminé, je supprime le "Tag" unique de toutes les lignes (la chaîne de remplacement est vide):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

12

Puisque personne d'autre n'a donné de réponse directe à la question qui a été posée , je vais le faire.

La réponse est qu'avec POSIX grep, il est impossible de satisfaire littéralement cette demande:

grep "<Regex for 'doesn't contain hede'>" input

La raison en est que POSIX grepn'est requis que pour travailler avec les expressions régulières de base , qui ne sont tout simplement pas assez puissantes pour accomplir cette tâche (elles ne sont pas capables d'analyser les langues normales, en raison du manque d'alternance et de parenthèses).

Cependant, GNU grepimplémente des extensions qui le permettent. En particulier, \|est l'opérateur d'alternance dans la mise en œuvre de BRE par GNU, et \(et \)sont les parenthèses. Si votre moteur d'expression régulière prend en charge l'alternance, les expressions de parenthèses négatives, les parenthèses et l'étoile Kleene, et est capable d'ancrer au début et à la fin de la chaîne, c'est tout ce dont vous avez besoin pour cette approche. Notez cependant que les jeux négatifs [^ ... ]sont très pratiques en plus de ceux-ci, car sinon, vous devez les remplacer par une expression de la forme (a|b|c| ... )qui répertorie tous les caractères qui ne sont pas dans le jeu, ce qui est extrêmement fastidieux et trop long, d'autant plus si l'ensemble des caractères est Unicode.

Avec GNU grep, la réponse serait quelque chose comme:

grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input

(trouvé avec Graal et quelques optimisations supplémentaires faites à la main).

Vous pouvez également utiliser un outil qui implémente les expressions régulières étendues , comme egreppour supprimer les barres obliques inverses:

egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input

Voici un script pour le tester (notez qu'il génère un fichier testinput.txtdans le répertoire courant):

#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"

# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede

h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)

Dans mon système, il imprime:

Files /dev/fd/63 and /dev/fd/62 are identical

comme prévu.

Pour ceux qui s'intéressent aux détails, la technique utilisée consiste à convertir l'expression régulière correspondant au mot en un automate fini, puis à inverser l'automate en changeant chaque état d'acceptation en non-acceptation et vice versa, puis en reconvertissant l'AF résultante en une expression régulière.

Enfin, comme tout le monde l'a noté, si votre moteur d'expression régulière prend en charge l'anticipation négative, cela simplifie beaucoup la tâche. Par exemple, avec GNU grep:

grep -P '^((?!hede).)*$' input

Mise à jour: J'ai récemment trouvé l'excellente bibliothèque FormalTheory de Kendall Hopkins , écrite en PHP, qui fournit une fonctionnalité similaire à Grail. En l'utilisant, et un simplificateur écrit par moi-même, j'ai pu écrire un générateur en ligne d'expressions régulières négatives avec une phrase d'entrée (seuls les caractères alphanumériques et spatiaux sont actuellement pris en charge): http://www.formauri.es/personal/ pgimeno / misc / non-match-regex /

Car hedeil délivre:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

qui est équivalent à ce qui précède.


11

Depuis l'introduction de ruby-2.4.1, nous pouvons utiliser le nouvel opérateur absent dans les expressions régulières de Ruby

du doc officiel

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

Ainsi, dans votre cas, ^(?~hede)$fait le travail pour vous

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

9

Par le verbe PCRE (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

Cela ignorerait complètement la ligne qui contient la chaîne exacte hedeet correspond à toutes les lignes restantes.

DEMO

Exécution des pièces:

Considérons le regex ci-dessus en le divisant en deux parties.

  1. Partie devant le |symbole. La pièce ne doit pas correspondre .

    ^hede$(*SKIP)(*F)
  2. Partie après le |symbole. La pièce doit être appariée .

    ^.*$

PARTIE 1

Le moteur Regex commencera son exécution à partir de la première partie.

^hede$(*SKIP)(*F)

Explication:

  • ^ Affirme que nous sommes au début.
  • hede Correspond à la chaîne hede
  • $ Affirme que nous sommes à la fin de la ligne.

Ainsi, la ligne qui contient la chaîne hedeserait mise en correspondance. Une fois que le moteur d'expression régulière voit le verbe suivant (*SKIP)(*F)( Remarque: vous pourriez écrire (*F)comme(*FAIL) ), il saute et fait échouer la correspondance. |appelé altération ou opérateur OU logique ajouté à côté du verbe PCRE qui correspond à son tour toutes les frontières existent entre chaque caractère sur toutes les lignes sauf que la ligne contient la chaîne exacte hede. Voir la démo ici . Autrement dit, il essaie de faire correspondre les caractères de la chaîne restante. Maintenant, l'expression régulière dans la deuxième partie serait exécutée.

PARTIE 2

^.*$

Explication:

  • ^ Affirme que nous sommes au début. c'est-à-dire qu'il correspond à tous les départs de ligne, sauf celui de la hedeligne. Voir la démo ici .
  • .*En mode multiligne, .correspondrait à n'importe quel caractère à l'exception des caractères de retour à la ligne ou de retour chariot. Et *répéterait le caractère précédent zéro ou plusieurs fois. Il en .*serait de même pour toute la ligne. Voir la démo ici .

    Hey pourquoi vous avez ajouté. * Au lieu de. +?

    Parce .*que correspondrait à une ligne vide mais .+ne correspondra pas à un blanc. Nous voulons faire correspondre toutes les lignes sauf hede, il peut y avoir une possibilité de lignes vides également dans l'entrée. vous devez donc utiliser .*au lieu de .+. .+répéterait le caractère précédent une ou plusieurs fois. Voir .*correspond à une ligne vide ici .

  • $ L'ancrage de fin de ligne n'est pas nécessaire ici.


7

Il peut être plus facile à gérer pour deux expressions régulières dans votre code, une pour effectuer la première correspondance, puis si elle correspond, exécutez la deuxième expression régulière pour vérifier les cas aberrants que vous souhaitez bloquer par exemple, ^.*(hede).*puis disposez d'une logique appropriée dans votre code.

OK, j'admets que ce n'est pas vraiment une réponse à la question publiée et qu'il peut également utiliser un peu plus de traitement qu'une seule expression régulière. Mais pour les développeurs qui sont venus ici à la recherche d'une solution d'urgence rapide pour un cas aberrant, cette solution ne doit pas être négligée.


6

Une autre option est que pour ajouter une anticipation positive et vérifier si se hehetrouve n'importe où dans la ligne d'entrée, nous annulerions cela, avec une expression similaire à:

^(?!(?=.*\bhede\b)).*$

avec des limites de mots.


L'expression est expliquée dans le panneau supérieur droit de regex101.com , si vous souhaitez l'explorer / la simplifier / la modifier, et dans ce lien , vous pouvez voir comment elle correspondrait à certains exemples d'entrées, si vous le souhaitez.


Circuit RegEx

jex.im visualise les expressions régulières:

entrez la description de l'image ici


5

Le langage TXR prend en charge la négation des expressions rationnelles.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

Un exemple plus compliqué: faites correspondre toutes les lignes commençant par aet finissant par z, mais ne contenant pas la sous-chaîne hede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

La négation d'expression régulière n'est pas particulièrement utile en soi, mais lorsque vous avez également une intersection, les choses deviennent intéressantes, car vous avez un ensemble complet d'opérations d'ensemble booléennes: vous pouvez exprimer "l'ensemble qui correspond à ceci, sauf pour les choses qui correspondent à cela".


Notez qu'il s'agit également de la solution pour l'expression régulière basée sur ElasticSearch Lucene.
Wiktor Stribiżew

4

La fonction ci-dessous vous aidera à obtenir la sortie souhaitée

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

2

^ ((?! hede).) * $ est une solution élégante, sauf qu'il consomme des caractères que vous ne pourrez pas combiner avec d'autres critères. Par exemple, supposons que vous vouliez vérifier la non-présence de "hede" et la présence de "haha". Cette solution fonctionnerait car elle ne consommera pas de caractères:

^ (?!. \ bhede \ b) (? =. \ bhaha \ b)


1

Comment utiliser les verbes de contrôle de retour arrière de PCRE pour faire correspondre une ligne ne contenant pas de mot

Voici une méthode que je n'ai jamais vue utilisée auparavant:

/.*hede(*COMMIT)^|/

Comment ça fonctionne

Tout d'abord, il essaie de trouver "hede" quelque part dans la ligne. En cas de succès, à ce stade, (*COMMIT)indique au moteur non seulement de ne pas revenir en arrière en cas d'échec, mais également de ne pas tenter de correspondance supplémentaire dans ce cas. Ensuite, nous essayons de faire correspondre quelque chose qui ne peut pas correspondre (dans ce cas, ^).

Si une ligne ne contient pas "hede", la deuxième alternative, un sous-modèle vide, correspond avec succès à la chaîne du sujet.

Cette méthode n'est pas plus efficace qu'un lookahead négatif, mais j'ai pensé que je la mettrais ici au cas où quelqu'un la trouverait astucieuse et trouverait une utilisation pour d'autres applications plus intéressantes.


0

Une solution plus simple consiste à utiliser l'opérateur not !

Votre instruction if devra correspondre à "contient" et non à "exclut".

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

Je pense que les concepteurs de RegEx ont prévu de ne pas utiliser d'opérateurs.


0

Peut-être le trouverez-vous sur Google en essayant d'écrire une expression régulière capable de faire correspondre des segments d'une ligne (par opposition à des lignes entières) qui ne contiennent pas de sous-chaîne. M'a pris un certain temps pour comprendre, alors je vais partager:

Étant donné une chaîne: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

Je veux faire correspondre les <span>balises qui ne contiennent pas la sous-chaîne "bad".

/<span(?:(?!bad).)*?>correspondra <span class=\"good\">et <span class=\"ugly\">.

Notez qu'il existe deux ensembles (couches) de parenthèses:

  • L'intérieur est pour l'anticipation négative (ce n'est pas un groupe de capture)
  • L'extérieur a été interprété par Ruby comme un groupe de capture, mais nous ne voulons pas que ce soit un groupe de capture, alors j'ai ajouté?: À ses débuts et il n'est plus interprété comme un groupe de capture.

Démo en Ruby:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]

0

Avec ConyEdit , vous pouvez utiliser la ligne de commande cc.gl !/hede/pour obtenir des lignes qui ne contiennent pas la correspondance regex, ou utiliser la ligne de commande cc.dl /hede/pour supprimer les lignes qui contiennent la correspondance regex. Ils ont le même résultat.


0

Je voulais ajouter un autre exemple si vous essayez de faire correspondre une ligne entière qui contient la chaîne X , mais ne contient pas aussi chaîne Y .

Par exemple, disons que nous voulons vérifier si notre URL / chaîne contient des " gâteries savoureuses ", tant qu'elle ne contient pas de " chocolat " nulle part.

Ce modèle d'expression régulière fonctionnerait (fonctionne également en JavaScript)

^(?=.*?tasty-treats)((?!chocolate).)*$

(drapeaux globaux, multilignes dans l'exemple)

Exemple interactif: https://regexr.com/53gv4

Allumettes

(Ces URL contiennent des "gâteries savoureuses" et ne contiennent pas non plus de "chocolat")

  • example.com/tasty-treats/strawberry-ice-cream
  • example.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

Ne correspond pas

(Ces URL contiennent du "chocolat" quelque part - donc elles ne correspondent pas même si elles contiennent des "gâteries savoureuses")

  • example.com/tasty-treats/chocolate-cake
  • example.com/home-cooking/oven-roasted-chicken
  • example.com/tasty-treats/banana-chocolate-fudge
  • example.com/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.