Optimisation Regex amusant exercice pour les enfants! Prendre regex de Gnarf comme point de départ:
^(.)\1*(.)?(?:\1*\2*)*(.)?(?:\1*\2*\3*)*$
Je remarqué qu'il y * ont été imbriquées et séquentiels s ici, ce qui peut causer beaucoup de retours en arrière. Par exemple, dans 'abcaaax', il essayera de faire correspondre cette dernière chaîne de 'a's comme un simple \ 1 * de longueur 3, un \ 1 * de longueur deux suivi d'un seul \ 1, un \ 1 suivi d'un 2-longueur \ 1 *, ou trois simples \ 1s. Ce problème s'aggrave lorsque vous avez des chaînes plus longues, surtout quand, à cause de la regex, il n'y a rien qui empêche \ 1 d'être le même caractère que \ 2.
^(.)\1*(.)?(?:\1|\2)*(.)?(?:\1|\2|\3)*$
Cela a été plus de deux fois plus rapide que l'original, en testant sur le matcher PCRE de Python. (C'est plus rapide que de le configurer en PHP, désolé.)
Cela pose toujours un problème car (.)?
ne peut rien trouver, et continue avec le reste du match. \1|\2
correspondra toujours à \ 1 même s'il n'y a pas de \ 2 à faire correspondre, ce qui entraînera un retour arrière potentiel en essayant d'introduire les clauses \1|\2
et \1|\2|\3
plus tôt lorsqu'elles ne peuvent pas aboutir à une correspondance. Ceci peut être résolu en déplaçant le ?
optionalness autour de l'ensemble des clauses de suivi:
^(.)\1*(?:(.)(?:\1|\2)*(?:(.)(?:\1|\2|\3)*)?)?$
C'était deux fois plus vite à nouveau.
Il existe toujours un problème potentiel en ce que l'un des caractères \ 1, \ 2 et \ 3 peut être le même caractère, ce qui peut provoquer plus de retour arrière lorsque l'expression ne correspond pas. Cela arrêterait en utilisant un négatif pour correspondre préanalyse pas un caractère précédent:
^(.)\1*(?:(?!\1)(.)(?:\1|\2)*(?:(?!\1|\2)(.)(?:\1|\2|\3)*)?)?$
Cependant en Python avec mes données de test aléatoire, je ne l'ai pas remarqué une importante accélération de cette situation. Votre kilométrage peut varier en PHP en fonction des données de test, mais cela pourrait déjà être suffisant. Possessive-matching (* +) aurait pu aider si c'était disponible ici.
Aucun regex meilleurs résultats que le plus facile à lire autre Python:
len(set(s))<=3
La méthode analogue en PHP serait probablement avec count_chars:
strlen(count_chars($s, 3))<=3
Je ne l'ai pas testé la vitesse mais je m'attendrais beaucoup à ce que ce soit plus rapide que regex, en plus d'être beaucoup, beaucoup plus agréable à lire.
Donc, fondamentalement, je viens de perdre tout mon temps à jouer avec les regex. Ne perdez pas votre temps, cherchez d'abord des méthodes de cordes simples avant de recourir à regex!
Wow, merci pour toutes les suggestions. Je ne peux pas vous dire combien je suis reconnaissant pour l'aide. – simonwjackson