Principes de correspondance régulière et principes d'optimisation

La raison pour laquelle la régularité peut gérer un texte complexe est due à l'utilisation d'automates finis. Alors, qu’est-ce qu’un automate fini ? Un état fini signifie qu’un système a un nombre fini d’états et que différents états représentent des significations différentes. Automates signifie que le système peut transférer dans différents états selon les conditions correspondantes. À partir d'un état initial, la transition d'état est effectuée selon l'opération correspondante (telle que le jeu de caractères d'entrée), et atteint finalement l'état de fin (il peut y avoir un ou plusieurs états de fin).

L'implémentation spécifique des automates finis est appelée moteur régulier, qui comprend principalement DFA et NFA.NFA est divisé en NFA traditionnel et POSIX NFA.

DFA:确定性有穷自动机(Deterministic finite automaton)
NFA:非确定性有穷自动机(Non-deterministic finite automaton)

La façon dont fonctionne le moteur NFA consiste à examiner d'abord les expressions régulières, puis le texte, et les expressions régulières prennent la tête. Ce n'est pas le cas avec DFA. DFA examine d'abord le texte, puis l'expression régulière. Il est orienté texte.

De manière générale, le moteur DFA sera plus rapide, car pendant tout le processus de correspondance, la chaîne n'est lue qu'une seule fois, aucun retour en arrière ne se produit et les mêmes caractères ne sont pas testés deux fois. Autrement dit, le temps d’exécution du moteur DFA est généralement linéaire. Le moteur DFA garantit que la chaîne la plus longue possible correspond. Mais comme le moteur DFA ne contient qu'un état limité, il ne dispose d'aucune fonctionnalité de référence arrière et, comme il ne construit pas d'extensions explicites, il ne prend pas en charge la capture de sous-groupes.

NFA est basé sur les expressions et son moteur est implémenté à l'aide d'un algorithme de backtracking gourmand. NFA prend en charge les sous-groupes et les références arrière via des extensions spécifiques à la construction. Mais comme le moteur NFA fera marche arrière, c’est-à-dire qu’il comparera plusieurs fois la même partie de la chaîne. Par conséquent, dans le pire des cas, son fonctionnement peut être très lent.

Étant donné que le moteur NFA traditionnel rapporte « avec impatience » les résultats correspondants et renvoie la première correspondance trouvée, il peut en résulter des correspondances plus longues qui n'ont pas été découvertes. Par exemple, si vous utilisez le pos|posix régulier pour faire correspondre le texte posix, le NFA traditionnel trouve pos à partir du texte, pas posix, tandis que le POSIX NFA trouve posix.

POSIX NFA a peu d'applications, principalement quelques outils sous Unix/Linux. Le moteur POSIX NFA est similaire au moteur NFA traditionnel, mais la différence est que POSIX NFA continuera à revenir en arrière avant de trouver la correspondance la plus longue possible, c'est-à-dire qu'il essaiera de trouver la plus longue. Si les branches sont de la même longueur, le plus à gauche (« Le plus long à gauche »). Par conséquent, les moteurs POSIX NFA sont plus lents que les moteurs NFA traditionnels.

 Le retour en arrière est unique au moteur NFA et ne peut se produire que lorsque des quantificateurs ou des structures de branches à sélection multiple apparaissent dans l'expression régulière.

Après avoir appris les principes, cela nous aidera à écrire de meilleures expressions régulières. Il faut d'abord s'assurer que le fonctionnement régulier est correct avant d'optimiser les performances.

1. Méthodes pour tester les performances

Vous pouvez utiliser ipython pour tester les performances des expressions régulières. ipython est un outil interactif amélioré du shell Python qui peut être installé et utilisé sur macOS/Windows/Linux. C'est très utile lors du test d'expressions régulières. Par exemple, l'exemple suivant utilise un exemple pour tester la consommation de temps lors de la recherche de abc dans une chaîne.

In [1]: import re
In [2]: x = '-' * 1000000 + 'abc'
In [3]: timeit re.search('abc', x)
480 µs ± 8.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

2. Compilez les règles régulières à l'avance

Il existe généralement une méthode de « compilation » dans les langages de programmation. Nous pouvons utiliser cette méthode pour traiter les expressions régulières à l'avance, afin de ne pas avoir à construire l'automate à plusieurs reprises à chaque fois que nous l'utilisons, ce qui peut améliorer les performances des expressions régulières. correspondant à.

3. Essayez d'exprimer la plage de correspondance aussi précisément que possible

Par exemple, si nous voulons faire correspondre le contenu entre guillemets, en plus d'écrire ".+?", nous pouvons écrire "[^"]+". Utiliser [^"] est bien mieux que d'utiliser des points, bien que le mode gourmand est utilisé. Mais il n'aura pas le problème de faire correspondre les guillemets avec des points, puis de les recracher.

4. Extraire la partie publique

Grâce à l'étude ci-dessus du moteur NFA, je pense que vous devez comprendre qu'une expression telle que (abcd|abxy) peut être optimisée en ab(cd|xy), car NFA est dominé par la régularité, ce qui entraînera certaines parties de la chaîne être répété. Faire correspondre plusieurs fois affecte l’efficacité.

Par conséquent, nous saurons que th(?:is|at) est plus rapide que this|that, mais du point de vue de la lisibilité, ce dernier est meilleur. Cela doit être pris en compte lors de son utilisation. Vous pouvez également ajouter des commentaires de code pour faire le code Plus facile à comprendre.

De même, s'il s'agit d'un point d'ancrage, tel que (^this|^that), la partie du point d'ancrage doit également être indépendante et peut être écrite sous la forme ^th(is|at) is, car la partie du point d'ancrage doit également être essayé.Pour la correspondance, le nombre de correspondances doit être aussi petit que possible.

5. Placez les éléments les plus susceptibles de se produire sur la gauche.

Puisque les règles habituelles sont vues de gauche à droite, celles avec la plus forte probabilité d'apparition sont placées à gauche. Dans les noms de domaine, .com est plus utilisé que .net, nous pouvons donc l'écrire sous la forme \.(?:com| net)\b au lieu de \ .(?:net|com)\b.

6. Utilisez des sous-groupes uniquement lorsque cela est nécessaire

Dans les expressions régulières, les parenthèses peuvent être utilisées pour le regroupement, mais si une certaine partie ne sera pas utilisée ultérieurement, il n'est pas nécessaire de la sauvegarder en tant que sous-groupe. L'approche habituelle consiste à ajouter ?: aux parenthèses qui n'ont pas besoin de sauvegarder les sous-groupes après avoir écrit les règles normales pour indiquer qu'elles ne sont utilisées que pour le regroupement. S'il est enregistré en tant que sous-groupe, le moteur standard doit effectuer un travail supplémentaire pour enregistrer le contenu correspondant, car il peut être utilisé ultérieurement, ce qui réduira les performances de correspondance régulière.

7. Méfiez-vous des duplications de sous-groupes imbriqués

Si un groupe contient des répétitions, alors le groupe entier peut également être répété. Par exemple, le modèle régulier (.*)* augmentera le nombre de correspondances de manière exponentielle, alors essayez de ne pas écrire de tels modèles réguliers.

8. Évitez les correspondances répétées de différentes branches

Dans la sélection de branches à sélection multiple, il est nécessaire d'éviter la même plage dans différentes branches. Dans l'exemple de backtracking ci-dessus, nous l'avons expliqué plus en détail.

Cet article est une note d'étude pour le 28 août. Le contenu provient du cours "Introduction aux expressions régulières" de Geek Time . Ce cours est recommandé.

Je suppose que tu aimes

Origine blog.csdn.net/key_3_feng/article/details/132548997
conseillé
Classement