web-dev-qa-db-fra.com

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

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

Donc; quelle est votre pensée - à quoi ressemble une regex, qui ne sera jamais égalée par aucune chaîne, jamais!

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

Dans ce script, je définis un dictionnaire comme Dictionary<string, Regex>. Celui-ci 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 seule référence sur la façon dont ils doivent faire leur travail, l'un d'eux compare les expressions rationnelles avec un fichier journal analysé.

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

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

Voilà donc ma raison pour laquelle "ce n'est pas une vraie question" ...

116
Florian Peschka

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

$a

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

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


* À 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 pourra jamais apparaître après $.

61
Ferdinand Beyer

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 en 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 les performances: sans elle, la chaîne entière sera scannée. Ceux qui n'utilisent pas Python voudra ajouter explicitement l'ancre:

\A(?!x)x
72
Alex Martelli

Celui qui a été manqué:

^\b$

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

33
Mark Byers

regardez autour de vous:

(?=a)b

Pour les débutants en regex: le regard positif sur l'avenir (?=a) s'assure que le caractère suivant est a, mais ne modifie pas l'emplacement de recherche (ou n'inclut pas le 'a' dans la chaîne correspondante). Maintenant que le caractère suivant est confirmé être a, la partie restante de l'expression régulière (b) ne correspond que si le caractère suivant est b. Ainsi, cette expression régulière ne correspond que si un caractère est à la fois a et b en même temps.

33
Amarghosh

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

Il ne peut pas apparaître au milieu d'un mot, auquel nous le forçons.

29
P Shved

$.

.^

$.^

(?!)

19
Knio

Correspondance maximale

a++a

Au moins un a suivi d'un nombre quelconque de a, sans retour en arrière. Essayez ensuite de faire correspondre un autre a.

ou sous-expression indépendante

Cela revient à mettre a+ dans une sous-expression indépendante, suivie d'une autre a.

(?>a+)a
11
Brad Gilbert

Perl 5.10 prend en charge les mots de contrôle spéciaux appelés "verbes", qui sont inclus entre (*...) séquence. (Comparer avec (?...) séquence spéciale.) Parmi eux, il comprend (*FAIL) verb qui revient immédiatement de l'expression régulière.

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

10
Kang Seonghoon
\B\b

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

Ensemble, ils ne peuvent correspondre à aucune position.

Voir également:

9
Kobi

Que diriez-vous $^ ou peut-être (?!)?

8
Bob

Cela semble fonctionner:

$.
8
Jerry Fernholz

Cela ne fonctionnera pas pour Python et beaucoup d'autres langages, mais dans une expression régulière Javascript, [] est une classe de caractères valide qui ne peut pas être mise en correspondance. Les éléments suivants devraient donc é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 quant au moment où vous en auriez besoin, j'en avais besoin parce que j'avais besoin d'un repli pour un modèle compilé dynamiquement basé sur les entrées de l'utilisateur. Lorsque le modèle n'est pas valide, je dois le remplacer par un modèle qui ne correspond à rien. Simplifié, il ressemble à ceci:

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

Python ne l'acceptera pas, mais Perl:

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

Cette expression régulière devrait (théoriquement) essayer de faire correspondre un nombre infini (pair) de ws, car le premier groupe (le ()s) revient en lui-même. Perl ne semble émettre aucun avertissement, même sous use strict; use warnings;, donc je suppose que c'est au moins valide, et mes tests (minimaux) ne correspondent à rien, donc je les soumets pour votre critique.

4
Chris Lutz

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

4
Bart Kiers

Le plus rapide sera:

r = re.compile(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 ne commençant pas par le caractère que vous choisissez au lieu de `` a '' car elle ne correspondra pas après le premier caractère plutôt qu'après le second dans ces cas.

4
Adam Nelson

Tant de bonnes réponses!

Semblable à la réponse de @ nivk, je voudrais partager la comparaison des performances de Perl pour différentes variantes de regex qui ne correspondent jamais.

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

Vitesse d'expression régulière:

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/mots (100 000 mots anglais).

Vitesse d'expression régulière:

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, noyau Linux 4.13, Perl 5.26)

2
filiprem

Après avoir vu certaines de ces bonnes réponses, commentaire de @ arantius (concernant le timing $x contre x^ contre (?!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 la norme de ligne 275k de @ arantius, j'ai exécuté les tests suivants dans 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 conclusion de @ arantius, (?!x)x était parmi les les plus lents (~ 37 fois plus lents). La question de la vitesse dépend donc certainement de la mise en œuvre. Testez-le vous-même sur votre système prévu 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^'. Veuillez 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 exécuté ceci, j'ai oublié de raw les 3 dernières expressions, donc '\b' a é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 juste, il correspondra toujours à ce texte, mais je pensais que cela valait la peine d'être noté car je ne sais pas pourquoi il 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 aucune ligne 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
2
nivk

Je crois que

\Z RE FAILS! \A

couvre même les cas où l'expression régulière comprend des drapeaux comme MULTILINE, DOTALL etc.

>>> import re
>>> x=re.compile(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.

2
tzot
(*FAIL)

ou

(*F)

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

2

Regex vide

La meilleure expression régulière pour ne jamais correspondre à rien est une expression régulière vide. Mais je ne suis pas sûr que tous les moteurs regex acceptent cela.

Regex impossible

L'autre solution est de créer une expression rationnelle impossible. Je l'ai trouvé $-^ ne prend que deux étapes pour calculer quelle que soit la taille de votre texte ( https://regex101.com/r/yjcs1Z/1 ).

Pour référence:

  • $^ et $. prendre 36 étapes pour calculer -> O (1)
  • \b\B prend 1507 étapes sur mon échantillon et augmente avec le nombre de caractères de votre chaîne -> O (n)

Fil plus populaire sur cette question:

1
aeon

Peut être ça?

/$.+^/
1
Dan Breen

Tous les exemples impliquant une correspondance de limites suivent la même recette. Recette:

  1. Prenez l'un des égaliseurs de limites: ^, $,\b,\A,\Z,\z

  2. Faites à l'opposé de ce à quoi ils sont destinés

Exemples:

^ et\A sont destinés au début, alors ne les utilisez pas au début

^ --> .^
\A --> .\A

\ b correspond à une limite de Word, utilisez-le donc entre

\b --> .\b.

$,\Z et\z sont destinés à la fin, donc ne les utilisez pas à la fin

$ --> $.
\Z --> \Z.
\z --> \z.

D'autres impliquent l'utilisation de l'anticipation et de l'antériorité qui fonctionnent également avec la même analogie: si vous donnez une anticipation positive ou négative suivie de quelque chose de contraire

(?=x)[^x]
(?!x)x

Si vous donnez un regard positif ou négatif en suivant quelque chose d'en face

[^x](?<=x)
x(?<!x)

Leur pourrait être plus un tel modèle et plus de telles analogies.

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

et remplacez ... par tous les symboles imprimables;). C'est pour un fichier texte.

0
Drakosha

Qu'en est-il au lieu de regex, utilisez simplement une instruction if toujours fausse? En javascript:

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