Quel est le moyen le plus rapide de vérifier si une chaîne correspond à une expression régulière dans Ruby?
Mon problème est que je dois "egrep" à travers une énorme liste de chaînes pour trouver celles qui correspondent à une expression rationnelle donnée au moment de l'exécution. Je me soucie seulement de savoir si la chaîne correspond à l'expression rationnelle, pas où elle correspond, ni quel est le contenu des groupes correspondants. J'espère que cette hypothèse peut être utilisée pour réduire le temps que mon code passe à faire correspondre les expressions rationnelles.
Je charge l'expression rationnelle avec
pattern = Regexp.new(ptx).freeze
J'ai trouvé que string =~ pattern
c'est légèrement plus rapide que string.match(pattern)
.
Existe-t-il d'autres astuces ou raccourcis qui peuvent être utilisés pour rendre ce test encore plus rapide?
la source
Réponses:
À partir de Ruby 2.4.0, vous pouvez utiliser
RegExp#match?
:Regexp#match?
est explicitement répertorié comme une amélioration des performances dans les notes de publication de la version 2.4.0 , car il évite les allocations d'objets effectuées par d'autres méthodes telles queRegexp#match
et=~
:la source
Regexp#match?
est en effet au moins 50% plus rapide que les autres alternatives.Ceci est une simple référence:
Donc,
=~
c'est plus rapide mais cela dépend de ce que vous voulez avoir comme valeur renvoyée. Si vous voulez juste vérifier si le texte contient une expression régulière ou ne pas utiliser=~
la source
=~
c'est plus rapide quematch
, avec une augmentation des performances moins dramatique lorsque l'on fonctionne sur des expressions rationnelles plus grandes. Ce que je me demande, c'est s'il existe un moyen étrange de rendre cette vérification encore plus rapide, peut-être en exploitant une méthode étrange dans Regexp ou une construction étrange.!("test123" !~ /1/)
?"test123" =~ /1/
/1/.match?("test123")
est plus rapide que"test123" =~ /1/
si ce n'est que pour vérifier si le texte contient une expression régulière ou non.C'est la référence que j'ai exécutée après avoir trouvé quelques articles sur le net.
Avec la version 2.4.0, le gagnant est
re.match?(str)
(comme le suggère @ wiktor-stribiżew), sur les versions précédentes,re =~ str
semble être le plus rapide, bien qu'ilstr =~ re
soit presque aussi rapide.Résultats IRM 1.9.3-o551:
Résultats IRM 2.1.5:
Résultats IRM 2.3.3 (il y a une régression dans l'appariement des regex, semble-t-il):
Résultats IRM 2.4.0:
la source
/a+b/ =~ str
etstr =~ /a+b/
. C'est valable même en les itérant à travers des fonctions et je vois cela suffisamment valide pour être considéré comme meilleur que le stockage et le gel d'expressions régulières sur une variable. J'ai testé mon script avec ruby 1.9.3p547, ruby 2.0.0p481 et ruby 2.1.4p265. Il est possible que ces améliorations aient été apportées sur des correctifs ultérieurs, mais je n'ai pas encore l'intention de le tester avec des versions / correctifs antérieurs.!(re !~ str)
que c'était peut-être plus rapide, mais ce n'est pas le cas.Qu'en est-il
re === str
(comparaison de cas)?Puisqu'il évalue true ou false et n'a pas besoin de stocker les correspondances, de renvoyer l'index de correspondance et ce genre de choses, je me demande si ce serait un moyen encore plus rapide de faire la correspondance que
=~
.Ok, j'ai testé ça.
=~
est toujours plus rapide, même si vous avez plusieurs groupes de capture, mais il est plus rapide que les autres options.BTW, à quoi bon
freeze
? Je ne pouvais en mesurer aucune amélioration de performance.la source
freeze
n'apparaîtront pas dans les résultats car il se produit avant les boucles de référence et agit sur le modèle lui-même.Selon la complexité de votre expression régulière, vous pouvez simplement utiliser un simple découpage de chaîne. Je ne suis pas sûr de l'aspect pratique de cela pour votre application ou si cela offrirait ou non des améliorations de vitesse.
la source
Les moteurs d'expression régulière varient dans la façon dont ils implémentent les recherches, mais, en général, ancrent vos modèles pour la vitesse et évitent les correspondances gourmandes, en particulier lors de la recherche de longues chaînes.
La meilleure chose à faire, jusqu'à ce que vous soyez familiarisé avec le fonctionnement d'un moteur particulier, est de faire des benchmarks et d'ajouter / supprimer des ancres, d'essayer de limiter les recherches, d'utiliser des caractères génériques par rapport aux correspondances explicites, etc.
Le joyau fruité est très utile pour comparer rapidement les choses, car il est intelligent. Le code Benchmark intégré de Ruby est également utile, bien que vous puissiez écrire des tests qui vous trompent en ne faisant pas attention.
J'ai utilisé les deux dans de nombreuses réponses ici sur Stack Overflow, vous pouvez donc rechercher dans mes réponses et voir de nombreux petits trucs et résultats pour vous donner des idées sur la façon d'écrire du code plus rapidement.
La chose la plus importante à retenir est qu'il est mauvais d'optimiser prématurément votre code avant de savoir où se produisent les ralentissements.
la source
Pour compléter les réponses de Wiktor Stribiżew et Dougui, je dirais cela
/regex/.match?("string")
aussi vite que"string".match?(/regex/)
.Rubis 2.4.0 (10 000 000 ~ 2 sec)
Rubis 2.6.2 (100 000 000 ~ 20 sec)
Remarque: les temps varient, parfois
/regex/.match?("string")
est plus rapide et parfois"string".match?(/regex/)
, les différences peuvent uniquement être dues à l'activité de la machine.la source