Cela semble être assez trivial, mais je suis nouveau en Python et je veux le faire de la manière la plus pythonique.
Je veux trouver l'index correspondant à la nième occurrence d'une sous-chaîne dans une chaîne.
Il doit y avoir quelque chose d'équivalent à ce que JE VEUX faire, c'est-à-dire
mystring.find("substring", 2nd)
Comment pouvez-vous y parvenir en Python?
Réponses:
L'approche itérative de Mark serait la manière habituelle, je pense.
Voici une alternative avec le fractionnement de chaînes, qui peut souvent être utile pour rechercher des processus liés:
Et voici un one-liner rapide (et un peu sale, en ce sens que vous devez choisir une balle qui ne peut pas correspondre à l'aiguille):
la source
.rfind('XXX')
, mais cela s'effondrerait si'XXX'
apparaissait plus tard dans l'entrée de toute façon.Voici une version plus pythonique de la solution itérative simple:
Exemple:
Si vous voulez trouver la nième occurrence de chevauchement de
needle
, vous pouvez incrémenter de1
au lieu delen(needle)
, comme ceci:Exemple:
Ceci est plus facile à lire que la version de Mark et ne nécessite pas la mémoire supplémentaire de la version de fractionnement ou l'importation du module d'expression régulière. Il adhère également à quelques-unes des règles du Zen de python , contrairement aux différentes
re
approches:la source
Cela trouvera la deuxième occurrence de sous-chaîne dans la chaîne.
Edit: Je n'ai pas beaucoup réfléchi aux performances, mais une récursivité rapide peut aider à trouver la nième occurrence:
la source
n
occurrences de la sous-chaîne. (Dans ce cas, la valeur de retour parcourra périodiquement toutes les positions d'occurrence).Comprendre que l'expression régulière n'est pas toujours la meilleure solution, j'en utiliserais probablement une ici:
la source
(m.start() for m in re.finditer(r"ab",s))[2]
itertools.islice
fonction:next(islice(re.finditer(r"ab",s), 2, 2+1)).start()
Je propose des résultats d'analyse comparative comparant les approches les plus importantes présentées jusqu'à présent, à savoir @ bobince
findnth()
(basé surstr.split()
) vs @ tgamblin's ou @Mark Byersfind_nth()
(basé surstr.find()
). Je vais également comparer avec une extension C (_find_nth.so
) pour voir à quelle vitesse nous pouvons aller. Voicifind_nth.py
:Bien sûr, les performances sont plus importantes si la chaîne est volumineuse, alors supposons que nous souhaitons trouver la 1000001e nouvelle ligne ('\ n') dans un fichier de 1,3 Go appelé 'bigfile'. Pour économiser de la mémoire, nous aimerions travailler sur une
mmap.mmap
représentation objet du fichier:Il y a déjà le premier problème avec
findnth()
, puisque lesmmap.mmap
objets ne prennent pas en chargesplit()
. Nous devons donc copier tout le fichier en mémoire:Aie! Heureusement,
s
tient toujours dans les 4 Go de mémoire de mon Macbook Air, alors comparons-nousfindnth()
:Clairement une performance terrible. Voyons comment l'approche basée sur
str.find()
fait:Bien mieux! De toute évidence,
findnth()
le problème est qu'il est obligé de copier la chaîne pendantsplit()
, ce qui est déjà la deuxième fois que nous copions les 1,3 Go de données aprèss = mm[:]
. Voici le deuxième avantage defind_nth()
: Nous pouvons l'utilisermm
directement, de sorte qu'aucune copie du fichier ne soit requise:Il semble y avoir une petite pénalité de performance opérant sur
mm
vs.s
, mais cela montre que celafind_nth()
peut nous donner une réponse en 1,2 s par rapport aufindnth
total de 47 s.Je n'ai trouvé aucun cas où l'
str.find()
approche basée était significativement pire que l'str.split()
approche basée, donc à ce stade, je dirais que la réponse de @ tgamblin ou de @Mark Byers devrait être acceptée au lieu de celle de @ bobince.Lors de mes tests, la version
find_nth()
ci - dessus était la solution pure Python la plus rapide que je pouvais proposer (très similaire à la version de @Mark Byers). Voyons ce que nous pouvons faire de mieux avec un module d'extension C. Voici_find_nthmodule.c
:Voici le
setup.py
fichier:Installez comme d'habitude avec
python setup.py install
. Le code C joue ici un avantage puisqu'il se limite à trouver des caractères uniques, mais voyons à quelle vitesse cela est:Clairement encore un peu plus rapide. Fait intéressant, il n'y a aucune différence au niveau C entre les cas en mémoire et mmappés. Il est également intéressant de voir que
_find_nth2()
, qui est basé surstring.h
lamemchr()
fonction de bibliothèque de 's , perd contre la simple implémentation dans_find_nth()
: Les "optimisations" supplémentaires dansmemchr()
sont apparemment contre-productives ...En conclusion, l'implémentation dans
findnth()
(basée surstr.split()
) est vraiment une mauvaise idée, car (a) elle fonctionne terriblement pour des chaînes plus grandes en raison de la copie requise, et (b) elle ne fonctionne pas du tout sur lesmmap.mmap
objets. La mise en œuvre dansfind_nth()
(basée surstr.find()
) doit être préférée en toutes circonstances (et donc être la réponse acceptée à cette question).Il reste encore beaucoup à faire, car l'extension C a fonctionné presque 4 fois plus vite que le code Python pur, ce qui indique qu'il pourrait y avoir un cas pour une fonction de bibliothèque Python dédiée.
la source
Le moyen le plus simple?
la source
Je ferais probablement quelque chose comme ça, en utilisant la fonction find qui prend un paramètre d'index:
Ce n'est pas particulièrement pythonique je suppose, mais c'est simple. Vous pouvez le faire en utilisant la récursivité à la place:
C'est un moyen fonctionnel de le résoudre, mais je ne sais pas si cela le rend plus pythonique.
la source
for _ in xrange(n):
peut être utilisé à la place dewhile n: ... n-=1
return find_nth(s, x, n - 1, i + 1)
devrait êtrereturn find_nth(s, x, n - 1, i + len(x))
. Pas grand-chose, mais permet de gagner du temps de calcul.Cela vous donnera un tableau des indices de départ pour les correspondances à
yourstring
:Alors votre nième entrée serait:
Bien sûr, vous devez être prudent avec les limites d'index. Vous pouvez obtenir le nombre d'instances
yourstring
comme ceci:la source
Voici une autre approche utilisant re.finditer.
La différence est que cela ne regarde dans la botte de foin que dans la mesure où cela est nécessaire
la source
Voici une autre version
re
+itertools
qui devrait fonctionner lors de la recherche de astr
ou aRegexpObject
. J'admettrai librement que cela est probablement sur-conçu, mais pour une raison quelconque, cela m'a amusé.la source
Construire sur la réponse de modle13 , mais sans la
re
dépendance du module.Je souhaite un peu que ce soit une méthode de chaîne intégrée.
la source
la source
Fournir une autre solution "délicate", qui utilise
split
etjoin
.Dans votre exemple, nous pouvons utiliser
la source
la source
find_nth('aaa', 'a', 0)
retourne1
alors qu'il devrait revenir0
. Vous avez besoin de quelque chose commei = s.find(substr, i) + 1
puis revenezi - 1
.Solution sans utiliser de boucles ni de récursivité.
la source
Pour le cas particulier où vous recherchez la nième occurrence d'un caractère (c'est-à-dire une sous-chaîne de longueur 1), la fonction suivante fonctionne en construisant une liste de toutes les positions d'occurrences du caractère donné:
S'il y a moins d'
n
occurrences du caractère donné, cela donneraIndexError: list index out of range
.Ceci est dérivé de la réponse de @ Zv_oDD et simplifié pour le cas d'un seul caractère.
la source
La doublure de remplacement est excellente mais ne fonctionne que parce que XX et la barre ont la même longueur
Une bonne définition générale serait:
la source
Voici la réponse que vous voulez vraiment:
la source
Voici ma solution pour trouver l'
n
occurrance deb
in stringa
:C'est du Python pur et itératif. Pour 0 ou
n
qui est trop grand, il renvoie -1. C'est une doublure et peut être utilisé directement. Voici un exemple:la source
Def:
Utiliser:
Production:
la source
Que diriez-vous:
la source