Regex - comment faire correspondre tout sauf un modèle particulier

171

Comment écrire une expression régulière pour correspondre à une chaîne qui ne répond pas à un modèle particulier? Je suis confronté à une situation où je dois faire correspondre un motif (A et ~ B).

pas pas
la source
PCRE serait le meilleur pour cela: voir Regex Pattern to Match, Excluding when… / Except between . J'ai supprimé la findstrbalise car toutes les réponses ici ne sont pas valides pour la balise.
Wiktor Stribiżew

Réponses:

192

Vous pouvez utiliser une assertion anticipée:

(?!999)\d{3}

Cet exemple correspond à trois chiffres autres que 999.


Mais si vous n'avez pas d'implémentation d'expression régulière avec cette fonctionnalité (voir Comparaison des saveurs d'expressions régulières ), vous devez probablement créer vous-même une expression régulière avec les fonctionnalités de base.

Une expression régulière compatible avec une syntaxe de base uniquement serait:

[0-8]\d\d|\d[0-8]\d|\d\d[0-8]

Cela correspond également à toute séquence de trois chiffres qui ne l'est pas 999.

Gombo
la source
1
Look-ahead n'est pas une syntaxe d'expression régulière standard, c'est une extension Perl, elle ne fonctionnera qu'en Perl, PCRE (Perl-Compatible RegEx) ou d'autres implémentations non standard
Juliano
10
Ce n'est peut-être pas standard, mais la plupart des langues modernes ne le prennent-ils pas en charge? Quelle langue ne prend pas en charge les anticipations de nos jours?
Bryan Oakley
1
C'est vrai. Mais la plupart des versions de regex prennent en charge cette fonctionnalité (voir < regular-expressions.info/refflavors.html> ).
Gumbo
1
je pense que la dernière regex ne correspondrait pas non plus à 009, 019 ... etc
Sebastian Viereck
1
Lex standard pour C n'utilise pas les PCRE :-(
pieman72
30

Si vous souhaitez faire correspondre un mot A dans une chaîne et ne pas faire correspondre un mot B. Par exemple: Si vous avez un texte:

1. I have a two pets - dog and a cat
2. I have a pet - dog

Si vous voulez rechercher des lignes de texte qui ont un chien pour un animal de compagnie et qui n'ont PAS de chat, vous pouvez utiliser cette expression régulière:

^(?=.*?\bdog\b)((?!cat).)*$

Il ne trouvera que la deuxième ligne:

2. I have a pet - dog
Aleks
la source
Il n'a pas mentionné cela dans la question, mais l'OP utilise en fait la findstrcommande DOS . Il n'offre qu'un petit sous-ensemble des fonctionnalités que vous vous attendez à trouver dans un outil regex; lookahead n'en fait pas partie. (Je viens d'ajouter la balise findstr moi-même.)
Alan Moore
2
hm, oui, je l'ai trouvé maintenant dans l'un de ses commentaires sur les messages. J'ai vu Regex dans le titre. Quoi qu'il en soit, si quelqu'un trouve ce message lors de la recherche de la même expression régulière, comme je l'ai fait, cela pourrait peut-être être utile à quelqu'un :) merci pour vos commentaires
Aleks
15

Faites correspondre le modèle et utilisez la langue hôte pour inverser le résultat booléen de la correspondance. Ce sera beaucoup plus lisible et maintenable.

Ben S
la source
1
Ensuite, je me retrouve avec (~ A ou B) au lieu de (A et ~ B). Cela ne résout pas mon problème.
notnot
1
Pseudo-code: String toTest; if (toTest.matches (A) AND! toTest.matches (B)) {...}
Ben S
J'aurais dû être plus clair - les pièces ne sont pas totalement indépendantes. Si A correspond à une partie de la chaîne, alors nous nous soucions si ~ B correspond au reste (mais pas nécessairement au tout). C'était pour la fonction findstr en ligne de commande Windows, que j'ai trouvée limitée aux vraies expressions régulières, donc point discutable.
notnot
8

non pas, ressuscitant cette ancienne question parce qu'elle avait une solution simple qui n'était pas mentionnée. (Vous avez trouvé votre question en faisant des recherches pour une quête de primes regex .)

Je suis confronté à une situation où je dois faire correspondre un motif (A et ~ B).

L'expression régulière de base pour cela est terriblement simple: B|(A)

Vous ignorez simplement les correspondances globales et examinez les captures du groupe 1, qui contiendront A.

Un exemple (avec toutes les clauses de non-responsabilité concernant l'analyse du code HTML dans l'expression régulière): A est des chiffres, B est des chiffres dans <a tag

Le regex: <a.*?<\/a>|(\d+)

Démo (regardez le groupe 1 dans le volet inférieur droit)

Référence

Comment faire correspondre le modèle sauf dans les situations s1, s2, s3

Comment faire correspondre un motif à moins que ...

zx81
la source
Ça semble trop bon pour être vrai! Malheureusement, cette solution n'est pas universelle et elle échoue dans Emacs, même après avoir été remplacée \dpar [[:digit:]]. La première référence mentionne qu'elle est spécifique à Perl et PHP: "Il existe une variante utilisant une syntaxe spécifique à Perl et PHP qui accomplit la même chose."
miguelmorin
4

Le complément d'une langue standard est également une langue standard, mais pour le construire, vous devez créer le DFA pour la langue standard et transformer tout changement d'état valide en erreur. Voir ceci pour un exemple. Ce que la page ne dit pas, c'est qu'elle a été convertie /(ac|bd)/en /(a[^c]?|b[^d]?|[^ab])/. La conversion d'un DFA en une expression régulière n'est pas anodine. C'est plus facile si vous pouvez utiliser l'expression régulière inchangée et changer la sémantique dans le code, comme suggéré précédemment.

Juliano
la source
2
Si je parlais de regex réels, tout cela serait sans objet. Regex semble maintenant faire référence à l'espace nébuleux CSG-ish (?) De correspondance de motifs que la plupart des langages prennent en charge. Puisque je dois faire correspondre (A et ~ B), il n'y a aucun moyen de supprimer la négation et de tout faire en une seule étape.
notnot
Lookahead, comme décrit ci-dessus, l'aurait fait si findstr avait fait autre chose que les véritables expressions rationnelles DFA. Le tout est assez étrange et je ne sais pas pourquoi je dois faire ce style de ligne de commande (lot maintenant). C'est juste un autre exemple de mes mains liées.
notnot
1
@notnot: Vous utilisez findstr depuis Windows? Ensuite, vous avez juste besoin de / v. Comme: findstr Un fichier d'entrée | findstr / v B> outputfile.txt Le premier correspond à toutes les lignes avec A, le second correspond à toutes les lignes qui n'ont pas B.
Juliano
Merci! C'est en fait exactement ce dont j'avais besoin. Je n'ai pas posé la question de cette façon, cependant, je donne toujours la réponse à Gumbo pour la réponse plus générale.
notnot
1

motif - re

str.split(/re/g) 

renverra tout sauf le motif.

Testez ici

unigogo
la source
Vous voudrez probablement mentionner que vous devez vous inscrire à nouveau.
tomdemuyt
Une approche similaire utilise replace str.replace(/re/g, ''), alors il n'est pas nécessaire de les rejoindre. aussi si vous lancez une belle traîne \ s? comme str.replace(/\re\s?/g, '')alors vous vous débarrassez de tous les espaces en double que vous auriez du remplacement de quelque chose au milieu d'une chaîne
jakecraige
0

Ma réponse ici pourrait également résoudre votre problème:

https://stackoverflow.com/a/27967674/543814

  • Au lieu de Replace, vous utiliseriez Match.
  • Au lieu de groupe $1, vous liriez groupe $2.
  • Le groupe a $2été rendu non capturant là-bas, ce que vous éviteriez.

Exemple:

Regex.Match("50% of 50% is 25%", "(\d+\%)|(.+?)");

Le premier groupe de capture spécifie le modèle que vous souhaitez éviter. Le dernier groupe de capture capture tout le reste. Lisez simplement ce groupe $2,.

Timo
la source
0
(B)|(A)

puis utilisez ce que le groupe 2 capture ...

DW.
la source
Il n'a pas besoin de capturer B, il ne vise pas simplement à ignorer tous les modèles B.
hexicle