Regex pour une chaîne ne se terminant pas par un suffixe donné


190

Je n'ai pas été en mesure de trouver une expression régulière appropriée pour correspondre à une chaîne ne se terminant pas par une condition. Par exemple, je ne veux pas faire correspondre quoi que ce soit se terminant par un a.

Cela correspond

b
ab
1

Cela ne correspond pas

a
ba

Je sais que l'expression régulière devrait se terminer par $pour marquer la fin, même si je ne sais pas ce qui devrait la précéder.

Edit : La question originale ne semble pas être un exemple légitime pour mon cas. Alors: comment gérer plus d'un personnage? Dites quelque chose qui ne se termine pas par ab?

J'ai pu résoudre ce problème en utilisant ce fil :

.*(?:(?!ab).).$

Bien que l'inconvénient soit que cela ne correspond pas à une chaîne d'un caractère.


5
Il ne s'agit pas d' un doublon de la question liée - la correspondance avec uniquement la fin nécessite une syntaxe différente de celle de la correspondance n'importe où dans une chaîne. Regardez simplement la première réponse ici.
jaustin

Je suis d'accord, ce n'est pas un double de la question liée. Je me demande comment on peut supprimer les "marques" ci-dessus?
Alan Cabrera

Il n'y a pas de lien de ce genre que je puisse voir.
Alan Cabrera

Réponses:


252

Vous ne nous donnez pas le langage, mais si votre support de saveur regex regarde derrière l'assertion , voici ce dont vous avez besoin:

.*(?<!a)$

(?<!a)est une assertion de regard inversé qui garantit qu'avant la fin de la chaîne (ou de la ligne avec le mmodificateur), il n'y a pas le caractère "a".

Voir ici sur Regexr

Vous pouvez également facilement l'étendre avec d'autres caractères, car cette vérification de la chaîne n'est pas une classe de caractères.

.*(?<!ab)$

Cela correspondrait à tout ce qui ne se termine pas par "ab", voyez-le sur Regexr


1
Je ne connais pas RegexPAL, mais les expressions régulières sont différentes dans toutes les langues et les assertions de regard derrière sont une fonctionnalité avancée qui n'est pas prise en charge par tous.
stema

7
regexpal est un testeur de regex basé sur javascript et javascript ne prend pas en charge les assertions
lookbehind,

Lookbehinds ne sont pas pris en charge sur regexr (javascript)
Stealth Rabbi

1
Le manque de regard en arrière dans JS me fait pleurer. Si vous faites du côté serveur, vous pouvez probablement utiliser le module PCRE sur NPM ou similaire pour les utiliser directement (c'est un ensemble de liaisons donc je ne pense pas que vous puissiez l'utiliser front-end)
Eirik Birkeland

Plus de types d'assertions lookahead / lookbehind: stackoverflow.com/q/2973436/12484
Jon Schneider

76

Utilisez le symbole not ( ^):

.*[^a]$

Si vous mettez le ^symbole au début des crochets, cela signifie «tout sauf les choses entre crochets». $est simplement une ancre vers la fin.

Pour plusieurs personnages , placez-les tous dans leur propre jeu de caractères:

.*[^a][^b]$

1
+1, avec la mise en garde que cela ne correspond pas à la chaîne vide (qui peut ou non être comme prévu), donc la signification est plutôt "tout caractère qui n'est pas entre crochets".
Fred Foo

3
@ 0A0D: une chaîne contenant des espaces n'est pas une chaîne vide.
Fred Foo

7
@ 0A0D En fait, ce n'est pas à débattre, c'est un fait
tckmn

8
@Doorknob: cela ne correspond pas aeou cb.
Fred Foo

1
Non, cela ne permettrait pas non plus "acb".
Menno

49

Pour rechercher des fichiers ne se terminant pas par ".tmp", nous utilisons l'expression régulière suivante:

^(?!.*[.]tmp$).*$

Testé avec le testeur Regex donne le résultat suivant:

entrez la description de l'image ici


1
C'est intéressant, une idée de pourquoi cela fonctionne et pourquoi ^.*(?![.]tmp$)pas?
Łukasz Zaroda

4
Votre early .*correspond déjà à toute la chaîne, donc l'exclusion restante ne fonctionne plus.
FiveO

Pour mes besoins, cela a fonctionné et les autres réponses n'ont pas fonctionné. Merci!
David Moritz

8
.*[^a]$

l'expression régulière ci-dessus correspondra aux chaînes qui ne se terminent pas par a.


J'ai étendu ma question car l'exemple original ne semblait pas correspondre entièrement à mon cas. Pouvez-vous le résoudre?
Menno

5

Essaye ça

/.*[^a]$/

Le []désigne une classe de caractères et l' ^inverse la classe de caractères pour correspondre à tout sauf un a.


1

La question est ancienne mais je n'ai pas pu trouver de meilleure solution je poste la mienne ici. Trouvez toutes les clés USB sans lister les partitions , supprimant ainsi la «partie [0-9]» des résultats. J'ai fini par faire deux grep, le dernier annule le résultat:

ls -1 /dev/disk/by-path/* | grep -P "\-usb\-" | grep -vE "part[0-9]*$"

Cela se traduit sur mon système:

pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0

Si je veux seulement les partitions, je pourrais faire:

ls -1 /dev/disk/by-path/* | grep -P "\-usb\-" | grep -E "part[0-9]*$"

Où j'arrive:

pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0-part1
pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0-part2

Et quand je fais:

readlink -f /dev/disk/by-path/pci-0000:00:0b.0-usb-0:1:1.0-scsi-0:0:0:0

Je reçois:

/dev/sdb

1

La réponse acceptée est bonne si vous pouvez utiliser des lookarounds. Cependant, il existe également une autre approche pour résoudre ce problème.

Si nous regardons le regex largement proposé pour cette question:

.*[^a]$

Nous verrons que cela fonctionne presque . Il n'accepte pas une chaîne vide, ce qui pourrait être un peu gênant. Cependant, il s'agit d'un problème mineur lorsqu'il s'agit d'un seul personnage. Cependant, si nous voulons exclure la chaîne entière, par exemple "abc", alors:

.*[^a][^b][^c]$

ne fera pas. Il n'acceptera pas de courant alternatif, par exemple.

Il existe cependant une solution simple à ce problème. On peut simplement dire:

.{,2}$|.*[^a][^b][^c]$

ou version plus généralisée:

.{,n-1}$|.*[^firstchar][^secondchar]$ où n est la longueur de la chaîne que vous voulez interdire (car abcc'est 3), et firstchar,, secondchar... sont les premier, deuxième ... nième caractères de votre chaîne (car abcce serait a, alors b, alors c).

Cela vient d'une simple constatation qu'une chaîne plus courte que le texte que nous n'interdirons pas ne peut pas contenir ce texte par définition. Nous pouvons donc soit accepter tout ce qui est plus court ("ab" n'est pas "abc"), ou tout ce qui est assez long pour que nous l'acceptions mais sans la fin.

Voici un exemple de recherche qui supprimera tous les fichiers qui ne sont pas .jpg:

find . -regex '.{,3}$|.*[^.][^j][^p][^g]$' -delete


.{,2}$|.*[^a][^b][^c]$ne correspond pasccc
psalaets

0

Tout ce qui correspond à quelque chose se terminant par un --- .*a$Ainsi, lorsque vous correspondez à l'expression régulière, annulez la condition ou vous pouvez également faire .*[^a]$[^a]signifie tout ce qui estnot a


0

Si vous utilisez grepou sedla syntaxe sera un peu différente. Notez que la [^a][^b]méthode séquentielle ne fonctionne pas ici:

balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n'
jd8a
8$fb
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a]$"
8$fb
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^b]$"
jd8a
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^c]$"
jd8a
8$fb
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a][^b]$"
jd8a
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a][^c]$"
jd8a
8$fb
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a^b]$"
q(c
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^a^c]$"
8$fb
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^b^c]$"
jd8a
balter@spectre3:~$ printf 'jd8a\n8$fb\nq(c\n' | grep ".*[^b^c^a]$"

FWIW, je trouve les mêmes résultats dans Regex101 , qui je pense est la syntaxe JavaScript.

Mauvais: https://regex101.com/r/MJGAmX/2
Bon: https://regex101.com/r/LzrIBu/2

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.