Je sais que je peux nier un groupe de caractères comme dans [^bar]
mais j'ai besoin d'une expression régulière dans laquelle la négation s'applique à un mot spécifique. Ainsi, dans mon exemple, comment nier un "bar"
réel et non "any chars in bar"
?
Une excellente façon de faire est d’utiliser lookahead négatif :
^(?!.*bar).*$
La construction de lookahead négative est la paire de parenthèses, avec la parenthèse ouvrante suivie d'un point d'interrogation et d'un point d'exclamation. À l'intérieur du lookahead [se trouve tout motif de regex].
À moins que les performances ne soient au cœur des préoccupations, il est souvent plus simple de faire passer vos résultats dans un deuxième passage, en ignorant ceux qui correspondent aux mots que vous souhaitez nier.
Les expressions régulières signifient généralement que vous utilisez un script ou une tâche à faible performance. Recherchez donc une solution facile à lire, à comprendre et à gérer.
La regex suivante fera ce que vous voulez (dans la mesure où les regards négatifs et antérieurs sont supportés), en faisant correspondre les choses correctement; le seul problème est qu’il fait correspondre des caractères individuels (c’est-à-dire que chaque correspondance correspond à un seul caractère plutôt qu’à tous les caractères entre deux "barres" consécutives), ce qui peut entraîner un risque de surcharge supplémentaire si vous travaillez avec de très longues chaînes.
b(?!ar)|(?<!b)a|a(?!r)|(?<!ba)r|[^bar]
Vous pouvez soit utiliser un prospect négatif ou prospectif :
^(?!.*?bar).*
^(.(?<!bar))*?$
Ou utilisez simplement des bases:
^(?:[^b]+|b(?:$|[^a]|a(?:$|[^r])))*$
Celles-ci correspondent toutes à tout ce qui ne contient pas bar
.
Je suis tombé sur ce fil de discussion en essayant d'identifier une expression rationnelle pour la déclaration suivante en anglais:
Pour une chaîne d'entrée, faites correspondre tout à moins que cette chaîne d'entrée soit exactement 'bar'; Par exemple, je veux faire correspondre "barrière" et "disbar" ainsi que "foo".
Voici la regex je suis venu avec
^(bar.+|(?!bar).*)$
Ma traduction anglaise de l'expression rationnelle est "faire correspondre la chaîne si elle commence par" bar "et si elle contient au moins un autre caractère, ou si la chaîne ne commence pas par" bar ".
Solution:
^(?!.*STRING1|.*STRING2|.*STRING3).*$
xxxxxx OK
xxxSTRING1xxx KO (est-il souhaité ou non)
xxxSTRING2xxx KO (est-il souhaité ou non)
xxxSTRING3xxx KO (est-il souhaité ou non)
La réponse acceptée est Nice, mais c’est vraiment une solution de rechange au manque d’un opérateur de négation de sous-expression simple dans les expressions rationnelles. C'est pourquoi grep --invert-match
se ferme. Ainsi, dans * nixes, vous pouvez obtenir le résultat souhaité à l'aide de pipes et d'une seconde expression régulière.
grep 'something I want' | grep --invert-match 'but not these ones'
Encore une solution de contournement, mais peut-être plus facile à retenir.
Je souhaite compléter la réponse acceptée et contribuer à la discussion avec ma réponse tardive.
@ChrisVanOpstal shared ce tutoriel sur les expressions rationnelles , qui est une excellente ressource pour apprendre les expressions rationnelles.
Cependant, la lecture a pris beaucoup de temps.
J'ai fait une feuille de triche pour la commodité mnémonique.
Cette référence est basée sur les accolades []
, ()
et {}
conduisant chaque classe et je trouve qu'il est facile de les rappeler.
Regex = {
'single_character': ['[]', '.', {'negate':'^'}],
'capturing_group' : ['()', '|', '\\', 'backreferences and named group'],
'repetition' : ['{}', '*', '+', '?', 'greedy v.s. lazy'],
'anchor' : ['^', '\b', '$'],
'non_printable' : ['\n', '\t', '\r', '\f', '\v'],
'shorthand' : ['\d', '\w', '\s'],
}
J'avais une liste de noms de fichiers et je voulais en exclure certains, avec ce genre de comportement (Ruby):
files = [
'mydir/states.rb', # don't match these
'countries.rb',
'mydir/states_bkp.rb', # match these
'mydir/city_states.rb'
]
excluded = ['states', 'countries']
# set my_rgx here
result = WankyAPI.filter(files, my_rgx) # I didn't write WankyAPI...
assert result == ['mydir/city_states.rb', 'mydir/states_bkp.rb']
Voici ma solution:
excluded_rgx = excluded.map{|e| e+'\.'}.join('|')
my_rgx = /(^|\/)((?!#{excluded_rgx})[^\.\/]*)\.rb$/
Mes hypothèses pour cette application:
.rb
..
avant le .rb
.Je viens de penser à quelque chose d'autre qui pourrait être fait. C'est très différent de ma première réponse, comme il n'utilise pas d'expressions régulières, j'ai donc décidé de faire une deuxième réponse.
Utilisez l'équivalent de méthode split()
de la langue de votre choix sur la chaîne avec le mot à nier en tant qu'argument sur lequel diviser. Un exemple utilisant Python:
>>> text = 'barbarasdbarbar 1234egb ar bar32 sdfbaraadf'
>>> text.split('bar')
['', '', 'asd', '', ' 1234egb ar ', '32 sdf', 'aadf']
La bonne chose à propos de le faire de cette façon, dans Python au moins (je ne me souviens pas si la fonctionnalité serait la même dans, par exemple, Visual Basic ou Java), c’est qu’elle vous permet de savoir indirectement quand "bar" a été répété dans la chaîne en raison du fait que les chaînes vides entre "bar" sont incluses dans la liste des résultats (bien que la chaîne vide au début soit due au fait qu'il y a un "bar" au début de la chaîne). Si vous ne le souhaitez pas, vous pouvez simplement supprimer les chaînes vides de la liste.
Extrait de ce commentaire par bkDJ :
^(?!bar$).*
La propriété de Nice de cette solution est qu'il est possible de clairement exclure (exclure) plusieurs mots:
^(?!bar$|foo$|banana$).*