168 votes

Une Regex qui ne sera jamais égalée par quoi que ce soit

Cette question peut sembler stupide, mais j'ai eu une longue discussion avec certains de mes collègues développeurs et cela m'a semblé être une chose amusante à envisager.

Alors, qu'en pensez-vous ? A quoi ressemble une Regex qui ne correspondra jamais à une chaîne de caractères, jamais !

Modifier : Pourquoi je veux ça ? Eh bien, d'abord parce que je trouve intéressant de penser à une telle expression et ensuite parce que j'en ai besoin pour un script.

Dans ce script, je définis un dictionnaire comme étant Dictionary<string, Regex> . Celui-ci contient, comme vous le voyez, une chaîne de caractères et une expression.

Sur la base de ce dictionnaire, je crée des méthodes qui utilisent toutes ce dictionnaire comme seule référence sur la manière dont elles doivent effectuer leur travail. L'une d'entre elles fait correspondre les regex à un fichier journal analysé.

Si une expression est trouvée, une autre Dictionary<string, long> est ajouté une valeur qui est retournée par l'expression. Donc, pour attraper tous les messages de log qui ne correspondent pas à une expression dans le dictionnaire, j'ai créé un nouveau groupe appelé "inconnu".

On ajoute à ce groupe tout ce qui ne correspond à rien d'autre. Mais pour éviter que l'expression "inconnue" ne corresponde pas (par accident) à un message de journal, j'ai dû créer une expression qui ne correspondra certainement jamais, quelle que soit la chaîne que je lui donne.

Ainsi, vous avez ma raison pour cette "pas une vraie question"...

1 votes

Notez qu'il est très difficile de prouver une négation.

0 votes

Wow, apparemment amusant signifie autre chose pour moi que pour les trois personnes qui ont rétrogradé ma réponse avant que je ne la supprime. Tu as raison, revenons aux choses sérieuses.

0 votes

Je ne pense pas qu'il aurait dû être fermé, mais je ne veux pas voter pour sa réouverture sans quelques informations de base.

87voto

Alex Martelli Points 330805

Effet de levier negative lookahead :

>>> import re
>>> x=r'(?!x)x'
>>> r=re.compile(x)
>>> r.match('')
>>> r.match('x')
>>> r.match('y')

ce RE est une contradiction dans les termes et ne correspondra donc jamais à rien.

NOTE :
En Python, re.match() ajoute implicitement une ancre de début de chaîne ( \A ) au début de l'expression régulière. Cette ancre est importante pour les performances : sans elle, la chaîne entière sera analysée. Ceux qui n'utilisent pas Python voudront ajouter l'ancre explicitement :

\A(?!x)x

0 votes

@Chris, yep -- aussi, (?=x)(?!x) et ainsi de suite (concaténations de lookaheads contradictoires, de même pour les lookbehinds), et beaucoup d'entre eux fonctionnent également pour des valeurs arbitraires de x (les regards ont besoin x qui correspondent à des chaînes de longueur fixe).

3 votes

Il semble que cela fonctionne bien. Mais que diriez-vous de simplement ( ?!) à la place ? Puisque () correspondra toujours, ( ?!) ne serait-il pas garanti de ne jamais correspondre ?

2 votes

@Peter, oui, si Python accepte cette syntaxe (et les versions récentes semblent le faire), alors ce serait également auto-contradictoire. Une autre idée (pas tout à fait aussi élégante, mais plus on a d'idées, plus on a de chances d'en trouver une qui fonctionne sur tous les moteurs RE qui nous intéressent) : r'a\bc' en recherchant une limite de mot immédiatement entourée de lettres des deux côtés (variante : caractères non-mots des deux côtés).

80voto

Ferdinand Beyer Points 27723

C'est en fait assez simple, bien que cela dépende de l'implémentation / des drapeaux * :

$a

Correspond à un caractère a après la fin de la chaîne. Bonne chance.

AVERTISSEMENT :
Cette expression est coûteuse -- elle va parcourir la ligne entière, trouver l'ancre de fin de ligne, et ensuite seulement ne pas trouver l'ancre de fin de ligne. a et renvoie une correspondance négative. (Voir le commentaire ci-dessous pour plus de détails).


* À l'origine, je n'ai pas beaucoup réfléchi aux regexp en mode multi-ligne, où $ correspond également à la fin d'une ligne. En fait, elle correspondrait à la chaîne vide juste avant le saut de ligne donc un caractère ordinaire comme a ne peut jamais apparaître après $ .

59 votes

Cette expression est coûteuse -- elle va scanner la ligne entière, trouver l'ancre de fin de ligne, et seulement ensuite ne pas trouver le "a" et retourner une correspondance négative. J'ai vu qu'il fallait ~480ms pour analyser un fichier de ~275k lignes. L'inverse "a^" prend à peu près le même temps, même si cela peut sembler plus efficace. D'un autre côté, un lookahead négatif n'a pas besoin de scanner quoi que ce soit : "(?!x)x" (tout ce qui n'est pas suivi d'un x est également suivi d'un x, c'est-à-dire rien) prend environ 30ms, soit moins de 7% du temps. (Mesuré avec gnu time et egrep.)

1 votes

En Perl, cela correspondra à la valeur actuelle de $a . C'est l'équivalent de Perl $(?:a) est également très lent perl -Mre=debug -e'$_=a x 50; /$(?:a)/' .

0 votes

@arantius , veuillez voir ma réponse concernant le timing car j'ai trouvé l'exact opposé mesuré avec timeit y python3 .

37voto

Amarghosh Points 33957

Regardez autour de vous :

(?=a)b

Pour les novices en matière de regex : Un regard positif sur l'avenir (?=a) s'assure que le caractère suivant est a mais ne modifie pas l'emplacement de la recherche (et n'inclut pas le "a" dans la chaîne de caractères recherchée). Maintenant, il est confirmé que le caractère suivant est a la partie restante de la regex ( b ) ne correspond que si le caractère suivant est b . Ainsi, cette regex ne correspond que si un caractère est à la fois a y b en même temps.

3 votes

... votre mouvement.

33voto

Pavel Shved Points 34706

a\bc\b est une expression de largeur nulle qui correspond aux limites des mots.

Il ne peut pas apparaître au milieu d'un mot, ce que nous lui imposons.

1 votes

Si votre cas d'utilisation vous permet d'ancrer le motif au début de la chaîne de caractères, cette amélioration empêchera le moteur regexp de rechercher et de tester chaque instance d'un motif a dans le texte.

25voto

Knio Points 1333

$.

.^

$.^

(?!)

1 votes

Mignon ! Mon subconscient m'a éloigné des idées comme les trois premières, car elles sont "illégales"... conceptuellement, mais évidemment pas pour le regex. Je ne reconnais pas le ( !)... je vais devoir le rechercher.

1 votes

Ok alors, j'aime la réponse ( ?!)... effectivement ce qu'Alex a suggéré. Notez que dans stackoverflow.com/questions/1723182 (souligné par Amarghosh ci-dessus) quelqu'un prétend que "certaines saveurs" de regex considéreraient cela comme une erreur de syntaxe. Python l'accepte pourtant sans problème. Notez que vos autres suggestions échoueraient toutes avec les modes re.DOTALL|re.MULTILINE en Python.

2 votes

Cela a-t-il été testé ? J'aurais supposé que ^ n'a de signification particulière que comme premier caractère d'une regexp, et $ n'a une signification particulière qu'à la fin d'une expression rationnelle, à moins que l'expression rationnelle ne soit une expression à plusieurs lignes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X