Quel est le moyen le plus efficace de trouver l'un de plusieurs sous-chaînes en Python?
J'ai une liste de sous-chaînes, par exemple, ['cat', 'poisson', 'chien']. Dans la pratique, la liste contient des centaines d'entrées.
Je suis le traitement d'une chaîne, et ce que je suis à la recherche est de trouver l'indice de la première comparution de l'une de ces sous-chaînes.
À clarifier, pour les "012cat" le résultat est 3, et le '0123dog789cat" le résultat est 4.
J'ai aussi besoin de savoir qui sous-chaîne a été trouvée (par exemple, son index dans la sous-chaîne de la liste ou le texte lui-même), ou au moins la longueur de la sous-chaîne de correspondance.
Il est manifeste que la force brute des moyens pour y parvenir, je me demandais si il n'y a aucune élégante de Python/Regex solution pour cela.
Grâce,
Rax
- C'est la liste de sous-chaînes constante? Je demande parce que, en utilisant les Regex type de solutions impliquent généralement certains precomputations de l'expression régulière (rsp. la liste de sous-chaînes dans votre cas). Serait-ce précalcul être amortis sur de nombreuses recherches?
Vous devez vous connecter pour publier un commentaire.
Je suppose une regex est mieux que de vérifier pour chaque sous-chaîne individuellement parce que conceptuellement l'expression régulière est modélisé comme un DFA, et donc que l'entrée est consommé tous les matchs sont en train d'être testés en même temps (en faisant une analyse de la chaîne d'entrée).
Donc, voici un exemple:
Mise à JOUR:
Certaines précautions doivent être prises lors de la combinaison des mots dans un modèle unique de mots alternatifs. Le code suivant crée une regex, mais échappe à toute regex caractères spéciaux et trie les mots de sorte que les mots plus longs à obtenir une chance de correspondre avant tout plus courte préfixes d'un même mot:
MISE À JOUR DE FIN
Il convient de noter que, vous voulez à la forme de l'expression rationnelle (c'est à dire - l'appel à re.compiler()) aussi peu que possible. Le meilleur des cas serait de vous savoir à l'avance ce que vos recherches sont (ou de vous calculer une fois/rarement) puis enregistrer le résultat de ré.compiler quelque part. Mon exemple n'est qu'un simple non-sens de la fonction de sorte que vous pouvez voir l'utilisation de la regex. Il ya un peu plus de la regex de docs ici:
http://docs.python.org/library/re.html
Espère que cette aide.
Mise à JOUR: je ne suis pas certain sur la façon python met en œuvre les expressions régulières, mais pour répondre à Rax la question de savoir si ou non il ya des limites de ré.compiler() (par exemple, combien de mots vous pouvez essayer de "|" ensemble pour correspondre à la fois), et le temps d'exécution de la compilation: aucune de ces semblent être un problème. J'ai essayé ce code, ce qui est assez bon pour m'en convaincre. (J'aurais pu faire mieux en ajoutant calendrier et la présentation des résultats, ainsi que de lancer la liste de mots dans un ensemble pour s'assurer il n'y a pas de doublons... mais ces deux améliorations sembler un peu exagéré). Ce code a couru essentiellement instantanément, et m'a convaincu que je suis en mesure de rechercher pour les 2000 mots (de taille 10), et que et de leur volonté de match de façon appropriée. Voici le code:
Mise à JOUR: Il convient de noter que l'ordre des choses par un ou binaire ensemble dans la regex questions. Regardez le test suivant, inspiré par TZOTZIOY:
Ceci suggère l'importance de l'ordre :-/. Je ne suis pas sûr de ce que cela signifie pour Rax de l'application, mais au moins, le comportement est connu.
Mise à JOUR: j'ai posté cette questions au sujet de la mise en œuvre d'expressions régulières en Python qui nous l'espérons, de nous donner quelques indications sur les problèmes rencontrés avec cette question.
Je veux juste souligner la différence de temps entre DisplacedAussie la réponse de Tom de la réponse. Les deux ont été rapidement lorsque utilisé une fois, vous ne devriez avoir aucun signe d'attendre, soit, mais quand vous le temps d'eux:
Sorties:
Je voudrais aller avec Tom réponse, tant pour des raisons de lisibilité, et de la vitesse.
C'est une vague réponse sans code fourni, mais j'espère que ça peut vous diriger dans la bonne direction.
Tout d'abord, vous aurez besoin d'un plus efficace de recherche pour votre sous-chaîne de la liste. Je recommande une sorte de structure de l'arbre. Commencer avec une racine, puis ajouter une
'a'
nœud si toutes les sous-chaînes de commencer avec'a'
, ajouter un'b'
nœud si toutes les sous-chaînes de commencer avec'b'
, et ainsi de suite. Pour chacun de ces nœuds, continuer à ajouter des sous-nœuds.Par exemple, si vous avez un sous-chaîne avec le mot "fourmi", vous devriez avoir un nœud racine, un nœud enfant
'a'
, un petit-enfant du nœud'n'
, et un arrière-petit-nœud't'
.Nœuds doit être assez facile à faire.
où
name
est un personnage.Parcourir vos chaînes lettre par lettre. Garder une trace de la lettre que vous êtes sur. À chaque lettre, essayez d'utiliser les quelques lettres de parcourir l'arbre. Si vous réussissez, votre numéro de lettre sera la position de la sous-chaîne, et votre traversée de commande indiquent la sous-chaîne qui a été trouvé.
Clarifier edit: DFAs devrait être beaucoup plus rapide cette méthode, et donc je devrait entériner Tom répondre. Je suis le seul à garder cette réponse dans le cas où votre sous-chaîne de la liste des changements souvent, dans les cas où l'utilisation d'un arbre pourrait être plus rapide.
Tout d'abord, je vous suggère de trier la liste dans l'ordre croissant. Car l'analyse pour une plus courte chaîne est plus rapide que le balayage pour une plus longue sous-chaîne.
Comment parler de celui-ci.
Évidemment, vous pourriez retourner autre chose qu'un n-uplet.
Cela fonctionne par: