Un regex qui ne sera jamais égalé par quoi que ce soit

Cela peut sembler une question stupide, mais j’ai eu une longue discussion avec certains de mes collègues développeurs et cela semblait être une chose amusante à penser.

Alors; quelle est votre pensée – à quoi ressemble une regex, qui ne sera jamais associée à aucune chaîne!

Edit : Pourquoi je veux ça? Eh bien, premièrement parce que je trouve intéressant de penser à une telle expression et deuxièmement parce que j’en ai besoin pour un script.

Dans ce script, je définis un dictionnaire comme Dictionary . Cela contient, comme vous le voyez, une chaîne et une expression.

Sur la base de ce dictionnaire, je crée des méthodes qui utilisent toutes ce dictionnaire comme référence uniquement sur la façon dont elles doivent faire leur travail, l’une d’entre elles correspondant aux expressions rationnelles par rapport à un fichier journal analysé.

Si une expression correspond, un autre Dictionary est ajouté à une valeur renvoyée par l’expression. Ainsi, pour intercepter les messages de consignation qui ne correspondent pas à une expression dans le dictionnaire, j’ai créé un nouveau groupe appelé «inconnu».

A ce groupe, tout ce qui ne correspond à rien d’autre est ajouté. Mais pour éviter que l’expression “inconnue” ne corresponde pas (par accident) à un message de consignation, j’ai dû créer une expression qui ne correspond certainement pas, quelle que soit la chaîne que je lui atsortingbue.

Ainsi, là vous avez ma raison pour cette “pas une vraie question” …

C’est en fait assez simple, même si cela dépend de l’implémentation / flags *:

 $a 

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

ATTENTION:
Cette expression est chère – elle parsingra toute la ligne, trouvera l’ancre de fin de ligne et ne trouvera alors que le a et retournera une correspondance négative. (Voir le commentaire ci-dessous pour plus de détails.)


* A l’ origine, je n’ai pas beaucoup réfléchi à l’expression rationnelle en mode multiligne, où $ correspond également à la fin d’une ligne. En fait, il correspondrait à la chaîne vide juste avant la nouvelle ligne , donc un caractère ordinaire comme a ne peut jamais apparaître après $ .

Effet de levier negative lookahead :

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

cette ER est une contradiction dans les termes et ne correspondra donc jamais à rien.

REMARQUE:
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 la performance: sans elle, la chaîne entière sera analysée. Ceux qui n’utilisent pas Python voudront append l’ancre explicitement:

 \A(?!x)x 

regardez autour de vous:

(?=a)b

Pour les débutants de regex: Le look positif anticipé (?=a) s’assure que le caractère suivant est a , mais ne change pas l’emplacement de recherche (ou inclut le ‘a’ dans la chaîne correspondante). Maintenant que le caractère suivant est confirmé, la partie restante de l’expression rationnelle ( 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 et b en même temps.

a\bc , où \b est une expression de largeur nulle qui correspond à la limite du mot.

Il ne peut pas apparaître au milieu d’un mot, ce qui nous oblige à le faire.

Celui qui a été manqué:

 ^\b$ 

Il ne peut pas correspondre car la chaîne vide ne contient pas de limite de mot. Testé en Python 2.5.

$.

.^

$.^

(?!)

Correspondance maximale

 a++a 

Au moins a suivi d’un nombre quelconque de a , sans retour en arrière. Ensuite, essayez de faire correspondre un autre a .

ou sous-expression indépendante

Cela équivaut à mettre a+ dans une sous-expression indépendante, suivi d’un autre a .

 (?>a+)a 

Perl 5.10 prend en charge des mots de contrôle spéciaux appelés “verbes”, qui sont entourés d’une séquence (*...) . (Comparer avec (?...) séquence spéciale.) Parmi eux, il inclut (*FAIL) verbe qui revient de l’expression régulière immédiatement.

Notez que les verbes sont également implémentés dans PCRE peu après, vous pouvez donc les utiliser en PHP ou dans d’autres langues en utilisant la bibliothèque PCRE. (Vous ne pouvez pas en Python ou Ruby, cependant, ils utilisent leur propre moteur.)

 \B\b 

\b correspond aux limites des mots – la position entre une lettre et une non-lettre (ou la limite de la chaîne).
\B est son complément – il correspond à la position entre deux lettres ou entre non-lettres.

Ensemble, ils ne peuvent correspondre à aucune position.

Voir également:

  • Frontières de mots
  • Ce motif ne correspond pas à quelques positions
  • Inspiration

Cela semble fonctionner:

 $. 

Qu’en est-il de $^ ou peut-être (?!) ?

Python ne l’acceptera pas, mais Perl:

 perl -ne 'print if /(w\1w)/' 

Cette regex devrait (théoriquement) essayer de faire correspondre un nombre infini (pair) de w s, car le premier groupe (le () s) est récursif en lui-même. Perl ne semble pas émettre d’avertissements, même sous use ssortingct; use warnings; use ssortingct; use warnings; , donc je suppose que c’est au moins valide et que mon test (minimal) ne correspond à rien, donc je le soumets à votre critique.

Le plus rapide sera:

 r = re.comstack(r'a^') r.match('whatever') 

“a” peut être n’importe quel caractère non spécial (“x”, “y”). L’implémentation de Knio pourrait être un peu plus pure mais celle-ci sera plus rapide pour toutes les chaînes qui ne commencent pas par le caractère que vous choisissez au lieu de “a” car il ne correspondra pas au premier caractère.

[^\d\D] ou (?=a)b ou a$a ou a^a

Cela ne fonctionnera pas pour Python, et pour beaucoup d’autres langages, mais dans une regex Javascript, [] est une classe de caractères valide qui ne peut pas être comparée. Donc, ce qui suit devrait échouer immédiatement, quelle que soit l’entrée:

 var noMatch = /^[]/; 

Je l’aime mieux que /$a/ parce que pour moi, il communique clairement son intention. Et en ce qui concerne le moment où vous en auriez besoin, j’en avais besoin car j’avais besoin d’un repli pour un modèle compilé dynamicment en fonction des entrées de l’utilisateur. Lorsque le motif est invalide, je dois le remplacer par un motif qui ne correspond à rien. Simplifié, cela ressemble à ceci:

 try { var matchPattern = new RegExp(someUserInput); } catch (e) { matchPattern = noMatch; } 

je crois que

 \Z RE FAILS! \A 

couvre même les cas où l’expression régulière comprend des indicateurs tels que MULTILINE, DOTALL etc.

 >>> import re >>> x=re.comstack(r"\Z RE FAILS! \A") >>> x.match('') >>> x.match(' RE FAILS! ') >>> 

Je crois (mais je ne l’ai pas évalué) que, quelle que soit la longueur (> 0) de la chaîne entre \Z et \A , le délai de défaillance doit être constant.

Après avoir vu certaines de ces excellentes réponses, le commentaire de @arantius (concernant le timing $x vs x^ vs (?!x)x ) sur la réponse actuellement acceptée m’a donné envie de chronométrer certaines des solutions proposées jusqu’à présent.

En utilisant le standard de ligne 275k de @arantius, j’ai effectué les tests suivants en Python (v3.5.2, IPython 6.2.1).

TL; DR: 'x^' et 'x\by' sont les plus rapides d’un facteur d’au moins ~ 16, et contrairement à la constatation de @arantius, (?!x)x était parmi les plus lents (~ 37 fois plus lent). La question de la vitesse dépend donc certainement de la mise en œuvre. Testez-le vous-même sur votre système avant de vous engager si la vitesse est importante pour vous.

MISE À JOUR: Il y a apparemment une grande différence entre le timing 'x^' et 'a^' . S’il vous plaît voir cette question pour plus d’informations, et l’édition précédente pour les timings plus lents avec a au lieu de x .

 In [1]: import re In [2]: with open('/tmp/longfile.txt') as f: ...: longfile = f.read() ...: In [3]: len(re.findall('\n',longfile)) Out[3]: 275000 In [4]: len(longfile) Out[4]: 24733175 In [5]: for regex in ('x^','.^','$x','$.','$x^','$.^','$^','(?!x)x','(?!)','(?=x)y','(?=x)(?!x)',r'x\by',r'x\bx',r'^\b$' ...: ,r'\B\b',r'\ZNEVERMATCH\A',r'\Z\A'): ...: print('-'*72) ...: print(regex) ...: %timeit re.search(regex,longfile) ...: ------------------------------------------------------------------------ x^ 6.98 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) ------------------------------------------------------------------------ .^ 155 ms ± 960 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ $x 111 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ $. 111 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ $x^ 112 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ $.^ 113 ms ± 1.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ $^ 111 ms ± 839 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ (?!x)x 257 ms ± 5.03 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) ------------------------------------------------------------------------ (?!) 203 ms ± 1.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ (?=x)y 204 ms ± 4.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) ------------------------------------------------------------------------ (?=x)(?!x) 210 ms ± 1.66 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) ------------------------------------------------------------------------ x\by 7.41 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) ------------------------------------------------------------------------ x\bx 7.42 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) ------------------------------------------------------------------------ ^\b$ 108 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ \B\b 387 ms ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) ------------------------------------------------------------------------ \ZNEVERMATCH\A 112 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) ------------------------------------------------------------------------ \Z\A 112 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

La première fois que j’ai couru ceci, j’ai oublié les trois dernières expressions, donc '\b' été interprété comme '\x08' , le caractère de retour arrière. Cependant, à ma grande surprise, 'a\x08c' était plus rapide que le résultat le plus rapide précédent! Pour être honnête, cela correspondra toujours à ce texte, mais je pensais que cela valait la peine de le noter car je ne suis pas sûr de savoir pourquoi c’est plus rapide.

 In [6]: for regex in ('x\by','x\bx','^\b$','\B\b'): ...: print('-'*72) ...: print(regex, repr(regex)) ...: %timeit re.search(regex,longfile) ...: print(re.search(regex,longfile)) ...: ------------------------------------------------------------------------ y 'x\x08y' 5.32 ms ± 46.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) None ------------------------------------------------------------------------ x 'x\x08x' 5.34 ms ± 66.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) None ------------------------------------------------------------------------ $ '^\x08$' 122 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) None ------------------------------------------------------------------------ \ '\\B\x08' 300 ms ± 4.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) None 

Mon fichier de test a été créé en utilisant une formule pour “… Contenu lisible et pas de lignes en double” (sur Ubuntu 16.04):

 $ ruby -e 'a=STDIN.readlines;275000.times do;b=[];rand(20).times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > /tmp/longfile.txt $ head -n5 /tmp/longfile.txt unavailable speedometer's garbling Zambia subcontracted fullbacks Belmont mantra's pizzicatos carotids bitch Hernandez renovate leopard Knuth coarsen Ramada flu occupies drippings peaces siroccos Bartók upside twiggier configurable perpetuates tapering pint paralyzed vibraphone stoppered weirdest dispute clergy's getup perusal fork nighties resurgence chafe 

Peut être ça?

 /$.+^/ 
 (*FAIL) 

ou

 (*F) 

Avec PCRE et PERL, vous pouvez utiliser ce verbe de contrôle de retour en arrière qui force le motif à échouer immédiatement.

Tant de bonnes réponses!

Semblable à la réponse de @ nivk, j’aimerais partager la comparaison des performances pour Perl pour différentes variantes de regex sans correspondance.

  1. Entrée: chaînes ascii pseudo-aléatoires (25 000 lignes différentes, longueur 8-16):

Vitesse de regex:

 Total for \A(?!x)x: 69.675450 s, 1435225 lines/s Total for a\bc: 71.164469 s, 1405195 lines/s Total for (?>a+)a: 71.218324 s, 1404133 lines/s Total for a++a: 71.331362 s, 1401907 lines/s Total for $a: 72.567302 s, 1378031 lines/s Total for (?=a)b: 72.842308 s, 1372828 lines/s Total for (?!x)x: 72.948911 s, 1370822 lines/s Total for ^\b$: 79.417197 s, 1259173 lines/s Total for $.: 88.727839 s, 1127041 lines/s Total for (?!): 111.272815 s, 898692 lines/s Total for .^: 115.298849 s, 867311 lines/s Total for (*FAIL): 350.409864 s, 285380 lines/s 
  1. Entrée: / usr / share / dict / words (100 000 mots anglais).

Vitesse de regex:

 Total for \A(?!x)x: 128.336729 s, 1564805 lines/s Total for (?!x)x: 132.138544 s, 1519783 lines/s Total for a++a: 133.144501 s, 1508301 lines/s Total for (?>a+)a: 133.394062 s, 1505479 lines/s Total for a\bc: 134.643127 s, 1491513 lines/s Total for (?=a)b: 137.877110 s, 1456528 lines/s Total for $a: 152.215523 s, 1319326 lines/s Total for ^\b$: 153.727954 s, 1306346 lines/s Total for $.: 170.780654 s, 1175906 lines/s Total for (?!): 209.800379 s, 957205 lines/s Total for .^: 217.943800 s, 921439 lines/s Total for (*FAIL): 661.598302 s, 303540 lines/s 

(Ubuntu sur Intel i5-3320M, kernel Linux 4.13, Perl 5.26)

 '[^0-9a-zA-Z...]*' 

et remplacer … par tous les symboles imprimables;). C’est pour un fichier texte.

Qu’en est-il à la place de regex, utilisez simplement une instruction if false? En javascript:

 var willAlwaysFalse=false; if(willAlwaysFalse) { } else { } 

Une solution portable qui ne dépendra pas de l’implémentation de regexp consiste à utiliser une chaîne constante dont vous êtes sûr qu’elle n’apparaîtra jamais dans les messages du journal. Par exemple, créez une chaîne basée sur les éléments suivants:

 cat /dev/urandom | hexdump | head -20 0000000 5d5d 3607 40d8 d7ab ce72 aae1 4eb3 ae47 0000010 c5e2 b9e8 910d a2d9 2eb3 fdff 6301 c85f 0000020 35d4 c282 e439 33d8 1c73 ca78 1e4d a569 0000030 8aca eb3c cbe4 aff7 d079 ca38 8831 15a5 0000040 818b 323f 0b02 caec f17f 387b 3995 88da 0000050 7b02 c80b 2d42 8087 9758 f56f b71f 0053 0000060 1501 35c9 0965 2c6e 03fe 7c6d f0ca e547 0000070 aba0 d5b6 c1d9 9bb2 fcd1 5ec7 ee9d 9963 0000080 6f0a 2c91 39c2 3587 c060 faa7 4ea4 1efd 0000090 6738 1a4c 3037 ed28 f62f 20fa 3d57 3cc0 00000a0 34f0 4bc2 3067 a1f7 9a87 086b 2876 1072 00000b0 d9e1 6b8f 5432 a60e f0f5 00b5 d9ef ed6f 00000c0 4a85 70ee 5ec4 a378 7786 927f f126 2ec2 00000d0 18c5 46fe b167 1ae6 c87c 1497 48c9 3c09 00000e0 8d09 e945 13ce 7da2 08af 1a96 c24c c022 00000f0 b051 98b3 2bf5 4d7d 5ec4 e016 a50d 355b 0000100 0e89 d9dd b153 9f0e 9a42 a51f 2d46 2435 0000110 ef35 17c2 d2aa 3cc7 e2c3 e711 d229 f108 0000120 324e 5d6a 650a d151 bc55 963f 41d3 66ee 0000130 1d8c 1fb1 1137 29b2 abf7 3af7 51fe 3cf4 

Bien sûr, ce n’est pas un défi intellectuel, mais plutôt un programme de ruban adhésif .

 new Regex(Guid.NewGuid().ToSsortingng()) 

Crée un modèle contenant uniquement des caractères alphanumériques et « - » (dont aucun ne sont des caractères spéciaux de regex), mais il est statistiquement impossible que la même chaîne soit apparue n’importe où auparavant (car c’est le but d’un GUID).