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


Je sais qu'il est possible de faire correspondre un mot, puis inverser les correspondances en utilisant d'autres outils (par ex. grep -v). Cependant, j'aimerais savoir s'il est possible de faire correspondre les lignes ne pas contient un mot spécifique (par exemple, hede) en 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

3559


origine


Réponses:


La notion que regex ne supporte pas la correspondance inverse n'est pas entièrement vraie. Vous pouvez imiter ce comportement en utilisant des consultations négatives:

^((?!hede).)*$

L'expression régulière ci-dessus correspondra à n'importe quelle chaîne ou ligne sans saut de ligne, ne pas contenant la (sous) chaîne 'hede'. Comme mentionné, ce n'est pas quelque chose que regex est "bon" (ou devrait faire), mais quand même, est possible.

Et si vous devez également faire correspondre les caractères de saut de ligne, utilisez Modificateur DOT-ALL (le suivi s dans le modèle suivant):

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

ou utilisez-le en ligne:

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

(où le /.../ sont les délimiteurs de regex, c'est-à-dire ne font 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 est juste une liste de n personnages. Avant et après chaque personnage, il y a une chaîne vide. Donc, une liste de n les personnages auront n+1 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ù le esont les chaînes vides. L'expression rationnelle (?!hede). regarde à l'avance pour voir s'il n'y a pas de sous-chaîne "hede" être vu, et si tel est le cas (donc quelque chose d'autre est vu), alors le . (point) correspondra à n'importe quel caractère sauf un saut de ligne. Look-arounds sont également appelés assertions de largeur nulle parce qu'ils ne le font pas consommer tous les caractères. Ils ne font qu'affirmer / valider quelque chose.

Donc, dans mon exemple, chaque chaîne vide est d'abord validée pour voir s'il n'y a pas "hede" en avant, avant qu'un personnage ne soit consommé par le . (point). L'expression rationnelle (?!hede). ne le fera qu'une seule fois, donc il est enveloppé dans un groupe, et répété zéro ou plusieurs fois: ((?!hede).)*. Enfin, les entrées de début et de fin sont ancrées pour s'assurer que toute l'entrée est consommée: ^((?!hede).)*$

Comme vous pouvez le voir, l'entrée "ABhedeCD" va échouer parce que sur e3, l'expression rationnelle (?!hede) échoue (il est  "hede" jusqu'à l'avant!).


4852



Notez que la solution à ne fait pas Commencer avec "Hede":

^(?!hede).*$

est généralement beaucoup plus efficace que la solution à ne fait pas contenir "Hede":

^((?!hede).)*$

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


603



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

ETA Oh, relire la question, grep -v est probablement ce que vous entendez par "options d'outils".


163



Répondre:

^((?!hede).)*$

Explication:

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

hedevotre chaîne,

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


121



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

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

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

Cela ne fait qu'un match complet. Le faire pour les sous-matches serait encore plus gênant.


89



Si vous voulez que le test de regex seulement échouer si le chaîne entière correspond à ce qui suit:

^(?!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 exact l'égalité, une meilleure solution générale dans ce cas est de vérifier l'égalité des chaînes, c.-à-d.

myStr !== 'foo'

Vous pourriez même mettre la négation à l'extérieur le test si vous avez besoin de fonctionnalités regex (ici, insensibilité à la casse et correspondance de plage):

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

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


48



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


47



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

Vcsn soutient cet opérateur (qu'il dénote {c}, postfixé).

Vous définissez d'abord le type de vos expressions: les étiquettes sont des lettres (lal_char) à choisir parmi a à z par exemple (définir l'alphabet lorsque l'on travaille avec la complémentation est, bien sûr, très important), et la "valeur" calculée pour chaque mot est juste un booléen: true le 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} → 𝔹

alors vous entrez votre expression:

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

convertir cette expression en un automate:

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

The corresponding automaton

enfin, convertissez 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 habituellement é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 essayez Vcsn en ligne .


43



Benchmarks

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 devraient pas correspondre, car elles contiennent l'expression recherchée, alors que les 7 lignes inférieures devraient 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 = mieux

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 supporte pas 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 en un petit gain de performance.

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


38



Avec lookahead négatif, 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. Bonne explication!

Cependant, avec la réponse de Bart Kiers, la partie lookahead testera 1 à 4 caractères à l'avance tout en correspondant à un seul personnage. Nous pouvons éviter cela et laisser la partie lookahead vérifier tout le texte, s'assurer qu'il n'y a pas de 'hede', et ensuite la partie normale (. *) Peut manger tout le texte en même temps.

Voici la regex améliorée:

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

Notez que le quantificateur paresseux (*?) Dans la partie lookahead négative est facultatif, vous pouvez utiliser (*) le quantificateur gourmand à la place, selon vos données: si 'hede' est présent et au début la moitié du texte, le quantificateur paresseux peut Être plus rapide; sinon, le quantificateur gourmand sera plus rapide. Cependant, si «hede» ne se présente pas, les deux seraient égaux.

Voici la code démo.

Pour plus d'informations sur lookahead, s'il vous plaît consulter le bon article: Maîtriser Lookahead et Lookbehind.

Aussi, s'il vous plaît vérifier RegexGen.js, un générateur JavaScript Expression régulière qui aide à construire des expressions régulières complexes. Avec RegexGen.js, vous pouvez construire l'expression rationnelle 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()
);

37



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


30