web-dev-qa-db-fra.com

Expression régulière pour faire correspondre une ligne qui ne contient pas un mot?

Je sais qu'il est possible de faire correspondre un mot, puis inverser les correspondances à l'aide d'autres outils (par exemple, grep -v). Cependant, j'aimerais savoir s'il est possible de faire correspondre des lignes qui ne [] contiennent pas un mot spécifique (par exemple hede) à l'aide d'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
3806
knaser

L'idée que regex ne supporte pas la correspondance inverse n'est pas tout à fait vraie. Vous pouvez imiter ce comportement en utilisant des points de contrôle négatifs:

^((?!hede).)*$

L'expression rationnelle ci-dessus correspond à n'importe quelle chaîne, ou ligne sans saut de ligne, et non contenant la (sous) chaîne 'hede'. Comme mentionné précédemment, ce n'est pas quelque chose de regex qui est "bon" (ou devrait faire), mais quand même, c'est est ​​possible.

Et si vous devez également faire correspondre les caractères de fin de ligne, utilisez le modificateur DOT-ALL (le s final dans le motif suivant):

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

ou utilisez-le en ligne:

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

(où les /.../ sont les délimiteurs de regex, c’est-à-dire qu’ils ne font pas partie du modèle)

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

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

Explication

Une chaîne est juste une liste de n caractères. Avant et après chaque caractère, il y a une chaîne vide. Ainsi, une liste de n caractères aura n+1 chaînes vides. Considérons 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 e sont les chaînes vides. La 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 (si quelque chose d'autre est vu), alors le . (point) correspondra n'importe quel caractère sauf un saut de ligne. Les look-arounds sont aussi appelés zéro-width-assertions car ils ne le font pas consume 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 a pas de "hede" avant, avant qu'un caractère ne soit consommé par le . (point). La regex (?!hede). ne le fera qu'une fois. Elle est donc encapsulée dans un groupe et répétée zéro ou plusieurs fois: ((?!hede).)*. Enfin, le début et la fin de l’entrée sont ancrés pour s’assurer que toute l’entrée est consommée: ^((?!hede).)*$

Comme vous pouvez le constater, l'entrée "ABhedeCD" échouera car sur e3, l'expression rationnelle (?!hede) échouera (là est ​​"hede" à venir!).

5492
Bart Kiers

Notez que la solution à ne pas commence par “hede”:

^(?!hede).*$

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

^((?!hede).)*$

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

689
JoshuaDavid

Si vous l'utilisez uniquement pour grep, vous pouvez utiliser grep -v hede pour obtenir toutes les lignes ne contenant pas hede.

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

178
Athena

Réponse:

^((?!hede).)*$

Explication:

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

hede votre chaîne, 

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

136
Jessica

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

Expressions régulières au sens de l'informatique théorique NE SONT PAS CAPABLES faites-le comme ceci. Pour eux, cela devait ressembler à quelque chose comme ça:

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

Cela ne fait qu'une correspondance complète. Le faire pour des sous-matchs serait encore plus délicat.

94
Hades32

Si vous voulez que le test de l'expression rationnelle ne contienne que seulement échec si la chaîne entière correspond, ce qui suit fonctionnera:

^(?!hede$).*

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

Bien sûr, si vous vérifiez l’égalité exact, une meilleure solution générale dans ce cas consiste à vérifier l’égalité des chaînes, c.-à-d. 

myStr !== 'foo'

Vous pouvez même mettre la négation en dehors de le test si vous avez besoin de fonctionnalités regex (ici, insensibilité à la casse et correspondance des plages):

!/^[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).

52
Roy Tinker

Voici une bonne explication pourquoi il n’est pas facile de nier une regex arbitraire. Je suis cependant d’accord avec les autres réponses: si c’est autre chose qu’une question hypothétique, alors une expression régulière n’est pas le bon choix ici.

50
Josh Lee

FWIW, étant donné que les langages normaux (ou rationnels) sont fermés sous complémentation, il est toujours possible de trouver une expression régulière (ou expression rationnelle) qui nie une autre expression. Mais peu d'outils implémentent cela.

Vcsn supporte cet opérateur (dénoté par {c}, postfix).

Vous définissez d’abord le type de vos expressions: les libellés sont des lettres (lal_char) à choisir parmi a à z par exemple (définir l’alphabet lorsque vous travaillez avec la complémentation est bien sûr très important), et la "valeur" calculée pour chaque mot juste un booléen: true la Parole est acceptée, false, rejetée.

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} → ????

alors vous entrez votre expression:

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

convertir cette expression en automate:

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

 The corresponding automaton

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 noté |, \e désigne le mot vide et [^] est généralement écrit . (tout 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 .

49
akim

Des 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. Benchmarking 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, alors que les 7 lignes les plus basses 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 exprimés en itérations par seconde et en tant que médiane de 3 analyses - Nombre supérieur = 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

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

Résumé:

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 étendue à ^(?>[^R]+|R+(?!egex Hero))*$ résultant dans un petit gain de performance.

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

41
Falco

Avec une anticipation négative, une 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 en attente testera de 1 à 4 caractères à l’avance tout en faisant correspondre un seul caractère. Nous pouvons éviter cela et laisser la partie attentive vérifier l'intégralité du texte, s'assurer qu'il n'y a pas de 'hede', et ensuite la partie normale (. *) Peut manger le texte entier en une seule fois.

Voici la regex améliorée:

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

Notez que le quantificateur paresseux (*?) Dans la partie lookahead négatif est optionnel, vous pouvez utiliser (*) un quantificateur glouton, en fonction de vos données: si 'hede' est présent et au début du texte, le quantificateur peut Être plus rapide; sinon, le quantificateur glouton sera plus rapide. Cependant, si 'hede' ne se présente pas, les deux seraient égaux lents.

Voici le code de démonstration .

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

Consultez également RegexGen.js , un générateur d’expressions régulières JavaScript qui permet de construire des expressions régulières complexes. Avec RegexGen.js, vous pouvez construire l’expression rationnelle de 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()
);
40
amobiz

Pas de regex, mais j'ai trouvé logique et utile d'utiliser des greps sériels avec pipe 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 de la série grep est (pas un commentaire) et (correspond à dir)

32
kiwalk

avec cela, vous évitez de tester un look sur chaque position:

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

équivalent à (pour .net):

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

Ancienne réponse:

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

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 de Friedl "déroulant la boucle" et nécessite beaucoup moins de retours en arrière.

19
ridgerunner

Le (?:(?!hede).)* mentionné ci-dessus est excellent 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 à avoir les clauses "ET" ajoutées:

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

Si vous souhaitez faire correspondre un caractère pour nier un mot similaire à une classe de caractère:

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);
?>

Remarquez que "(?!bbb)." n’est ni à l’arrière ni à l’avant, c’est par exemple

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

Le PO n'a pas spécifié ou Tag le post pour indiquer le contexte (langage de programmation, éditeur, outil) où Regex sera utilisé.

Pour moi, je dois parfois faire cela en éditant un fichier avec Textpad.

Textpad prend en charge certaines expressions rationnelles, mais ne prend pas en charge les fonctions lookahead ou lookbehind. Il faut donc quelques étapes.

Si je cherche à conserver toutes les lignes qui Ne pas contient la chaîne hede, je le ferais comme ceci:

1. Recherchez/remplacez le fichier entier pour ajouter une "balise" unique au début de chaque ligne contenant du 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 pas contient la chaîne hede. Supprimez la "balise" 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 original avec toutes les lignes contenant la chaîne hede supprimé.


Si je cherche à Faire autre chose aux seules lignes qui Ne pas contient la chaîne hede, je le ferais comme ceci:

1. Recherchez/remplacez le fichier entier pour ajouter une "balise" unique au début de chaque ligne contenant du texte.

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

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

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

3. A ce stade, toutes les lignes commençant par l'unique "Tag", Ne pas contient la chaîne hede. Je peux maintenant faire mon Autre chose à seulement ces lignes.

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

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

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

de l'officiel doc

(?~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
aelor

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

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 grep n’est nécessaire que pour travailler avec expressions régulières de base , qui ne sont tout simplement pas assez puissants pour accomplir cette tâche (ils ne sont pas capables d’analyser des langages normaux, faute d’alternance et de regroupement) .

Cependant, GNU grep implé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 opérateurs de regroupement. Si votre moteur d'expression régulière prend en charge l'alternance, les expressions entre crochets, le regroupement et l'étoile Kleene, et qu'il 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.

Avec GNU grep, ce 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 effectuées à la main).

Vous pouvez également utiliser un outil qui implémente expressions régulières étendues , comme egrep, pour 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.txt dans le répertoire actuel):

#!/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 les personnes intéressées par les détails, la technique employée consiste à convertir l'expression régulière qui correspond au mot en un automate fini, puis à inverser l'automate en modifiant chaque état d'acceptation en non-acceptation, et inversement, puis en reconvertissant le FA obtenu 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’analyse 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 avec un simplificateur écrit par moi-même, j'ai pu écrire un générateur en ligne d'expressions régulières négatives à partir d'une phrase d'entrée (seuls les caractères alphanumériques et les espaces sont actuellement pris en charge): http://www.formauri.es/personal/pgimeno/misc/non-match-regex /

Pour hede, il génère:

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

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

8
Pedro Gimeno

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

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

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

DEMO

Exécution des pièces:

Considérons la regex ci-dessus en la scindant en deux parties.

  1. Partie précédant le symbole |. La partie ne devrait pas être assortie

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

    ^.*$
    

PARTIE 1 

Le moteur de 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 hede serait vérifiée. Une fois que le moteur des expressions rationnelles détecte le verbe (*SKIP)(*F) (Remarque: vous pouvez écrire (*F) sous la forme (*FAIL)), il est ignoré et la correspondance échoue. | appelé altération ou opérateur logique OR ajouté à côté du verbe PCRE qui correspond actuellement à toutes les limites existant entre chaque caractère de toutes les lignes sauf la ligne contenant la chaîne exacte hede. Voir la démo ici . C'est-à-dire qu'il essaie de faire correspondre les caractères de la chaîne restante. Maintenant, la regex dans la deuxième partie serait exécutée.

PARTIE 2

^.*$

Explication:

  • ^ Affirme que nous sommes au début. c'est-à-dire qu'elle correspond à tous les débuts de ligne sauf celui de la ligne hede. Voir la démo ici .
  • .* En mode multiligne, . correspond à n'importe quel caractère sauf les caractères de retour à la ligne ou de retour à la ligne. Et * répète le caractère précédent zéro fois ou plus. Donc, .* correspondrait à la ligne entière. Voir la démo ici .

    Hey pourquoi tu as ajouté. * Au lieu de. +?

    Parce que .* correspondrait à une ligne vide mais .+ ne correspondra pas à une ligne vide. 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ète le caractère précédent une ou plusieurs fois. Voir .* correspond à une ligne vide ici .

  • $ La fin de l'ancre de ligne n'est pas nécessaire ici.

8
Avinash Raj

Il peut être plus facile de maintenir deux expressions rationnelles dans votre code, une pour faire la première correspondance, puis si elle correspond, exécutez la deuxième regex 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 postée et qu’il peut aussi prendre un peu plus de temps de traitement qu’une simple expression rationnelle. Mais pour les développeurs qui sont venus ici à la recherche d'un correctif d'urgence rapide pour un cas aberrant, cette solution ne doit pas être négligée.

6
andrew pate

Le langage TXR prend en charge la négation de regex.

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

Un exemple plus compliqué: correspond à toutes les lignes commençant par a et se terminant 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'une expression rationnelle 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 sur les ensembles booléens: vous pouvez exprimer "l'ensemble qui correspond à cela, sauf les choses qui correspondent à cela".

5
Kaz

La fonction ci-dessous vous aidera à obtenir le résultat souhaité

<?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;
    }


?>
3
Daniel Nyamasyo

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 "" - ainsi l'exigence est traduite presque directement en regex.

Bien entendu, il est possible d’avoir plusieurs exigences en matière d’échec:

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

Détails: L'ancre ^ garantit que le moteur des expressions rationnelles 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é représenter le début de la ligne. L'outil grep correspond à chaque ligne une par une. Dans les contextes dans lesquels vous travaillez avec une chaîne multiligne, vous pouvez utiliser l'indicateur "m":

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

ou

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

Une autre option consiste à ajouter une apparence positive et à vérifier si hehe se trouve n'importe où dans la ligne d'entrée. Nous nierons cela avec une expression similaire à:

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

avec limites de mots.


L'expression est expliquée dans le panneau en haut à droite de regex101.com , si vous souhaitez l'explorer/la simplifier/la modifier, et dans ce lien , vous pouvez voir comment cela se passerait. correspond à certains exemples d'entrées, si vous le souhaitez.


Circuit RegEx

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

enter image description here

1
Emma

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

0
Donald

Vous trouverez peut-être cela sur Google en essayant d'écrire une expression rationnelle capable de faire correspondre les segments d'une ligne (par opposition à des lignes entières) qui ne contiennent pas une sous-chaîne. Prends-moi un peu de 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 balises <span> 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:

  • Le plus intime est pour le look de tête négatif (ce n'est pas un groupe de capture)
  • Ruby a interprété la partie la plus externe comme un groupe de capture, mais nous ne voulons pas qu’il s’agisse d’un groupe de capture, j’ai donc ajouté?: Au début, il n’est plus interprété comme un groupe de capture.

Démo en rubis:

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
BrunoFacca

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 marche

Premièrement, il essaie de trouver "hede" quelque part dans la file. En cas de succès, (*COMMIT) indique alors au moteur non seulement de ne pas revenir en arrière en cas de panne, 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 à la chaîne de sujet.

Cette méthode n’est pas plus efficace que l’anticipation négative, mais j’ai pensé que je la mettrais ici au cas où quelqu'un la trouverait chouette et la trouve utile pour d’autres applications plus intéressantes.

0
jaytea

^ ((?! hede).) * $ est une solution élégante, sauf qu’elle 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 consommerait pas de caractères:

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

0
cloudhopperpilot

Une solution plus simple consiste à utiliser le pas opérateur!

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

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

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

Je crois que les concepteurs de RegEx ont prévu l’utilisation de non opérateurs.

0
JohnP2