Comment trouver plusieurs occurrences d'une chaîne dans une chaîne en Python? Considère ceci:
>>> text = "Allowed Hello Hollow"
>>> text.find("ll")
1
>>>
Donc, la première occurrence de ll
est à 1 comme prévu. Comment puis-je en trouver la prochaine occurrence?
La même question est valable pour une liste. Considérer:
>>> x = ['ll', 'ok', 'll']
Comment trouver tous les ll
avec leurs index?
En utilisant des expressions régulières, vous pouvez utiliser re.finditer
pour rechercher toutes les occurrences (ne se chevauchant pas):
>>> import re
>>> text = 'Allowed Hello Hollow'
>>> for m in re.finditer('ll', text):
print('ll found', m.start(), m.end())
ll found 1 3
ll found 10 12
ll found 16 18
Sinon, si vous ne souhaitez pas surcharger les expressions régulières, vous pouvez également utiliser plusieurs fois str.find
pour obtenir l'index next:
>>> text = 'Allowed Hello Hollow'
>>> index = 0
>>> while index < len(text):
index = text.find('ll', index)
if index == -1:
break
print('ll found at', index)
index += 2 # +2 because len('ll') == 2
ll found at 1
ll found at 10
ll found at 16
Cela fonctionne également pour les listes et autres séquences.
Je pense que ce que vous recherchez, c'est string.count
"Allowed Hello Hollow".count('ll')
>>> 3
J'espère que cela t'aides
NOTE: ceci ne capture que les occurrences ne se chevauchant pas
Pour l'exemple de liste, utilisez une compréhension:
>>> l = ['ll', 'xx', 'll']
>>> print [n for (n, e) in enumerate(l) if e == 'll']
[0, 2]
De même pour les chaînes:
>>> text = "Allowed Hello Hollow"
>>> print [n for n in xrange(len(text)) if text.find('ll', n) == n]
[1, 10, 16]
ceci listera les exécutions adjacentes de "ll", ce qui peut être ou ne pas être ce que vous voulez:
>>> text = 'Alllowed Hello Holllow'
>>> print [n for n in xrange(len(text)) if text.find('ll', n) == n]
[1, 2, 11, 17, 18]
FWIW, voici quelques alternatives non-RE qui, je pense, sont plus simples que la solution de poke .
Le premier utilise str.index
et vérifie ValueError
:
def findall(sub, string):
"""
>>> text = "Allowed Hello Hollow"
>>> Tuple(findall('ll', text))
(1, 10, 16)
"""
index = 0 - len(sub)
try:
while True:
index = string.index(sub, index + len(sub))
yield index
except ValueError:
pass
Le second test utilise str.find
et recherche la sentinelle de -1
en utilisant iter
:
def findall_iter(sub, string):
"""
>>> text = "Allowed Hello Hollow"
>>> Tuple(findall_iter('ll', text))
(1, 10, 16)
"""
def next_index(length):
index = 0 - length
while True:
index = string.find(sub, index + length)
yield index
return iter(next_index(len(sub)).next, -1)
Pour appliquer l'une de ces fonctions à une liste, un nuplet ou une autre chaîne iterable, vous pouvez utiliser une fonction niveau supérieur, qui prend une fonction comme l'un de ses arguments, comme celui-ci:
def findall_each(findall, sub, strings):
"""
>>> texts = ("fail", "dolly the llama", "Hello", "Hollow", "not ok")
>>> list(findall_each(findall, 'll', texts))
[(), (2, 10), (2,), (2,), ()]
>>> texts = ("parallellized", "illegally", "dillydallying", "hillbillies")
>>> list(findall_each(findall_iter, 'll', texts))
[(4, 7), (1, 6), (2, 7), (2, 6)]
"""
return (Tuple(findall(sub, string)) for string in strings)
Pour votre exemple de liste:
In [1]: x = ['ll','ok','ll']
In [2]: for idx, value in enumerate(x):
...: if value == 'll':
...: print idx, value
0 ll
2 ll
Si vous vouliez que tous les éléments de la liste contenant "ll", vous pouvez également le faire.
In [3]: x = ['Allowed','Hello','World','Hollow']
In [4]: for idx, value in enumerate(x):
...: if 'll' in value:
...: print idx, value
...:
...:
0 Allowed
1 Hello
3 Hollow
>>> for n,c in enumerate(text):
... try:
... if c+text[n+1] == "ll": print n
... except: pass
...
1
10
16
Une nouveauté dans la programmation en général et l’utilisation d’un didacticiel en ligne. On m'a également demandé de le faire, mais en utilisant uniquement les méthodes que j'avais apprises jusqu'à présent (essentiellement des chaînes et des boucles). Je ne sais pas si cela ajoute une quelconque valeur ici, et je sais que ce n'est pas comme cela que vous le feriez, mais je dois le faire fonctionner avec ceci
needle = input()
haystack = input()
counter = 0
n=-1
for i in range (n+1,len(haystack)+1):
for j in range(n+1,len(haystack)+1):
n=-1
if needle != haystack[i:j]:
n = n+1
continue
if needle == haystack[i:j]:
counter = counter + 1
print (counter)
This link explique comment faire le tout dans O(n) et inclut également une solution en python.
Si vous allez plus loin dans les ensembles jusqu'à ' Suffixer les arbres ', vous pourrez faire la même chose si vous avez une grosse chaîne mais que vous voulez rechercher des milliers de motifs.
Voici ma fonction pour trouver plusieurs occurrences. Contrairement aux autres solutions ici, il prend en charge les paramètres optionnels de début et de fin pour le découpage, exactement comme str.index
:
def all_substring_indexes(string, substring, start=0, end=None):
result = []
new_start = start
while True:
try:
index = string.index(substring, new_start, end)
except ValueError:
return result
else:
result.append(index)
new_start = index + len(substring)
Cette version doit avoir une longueur linéaire dans la chaîne et doit être correcte tant que les séquences ne sont pas trop répétitives (dans ce cas, vous pouvez remplacer la récursion par une boucle while).
def find_all(st, substr, start_pos=0, accum=[]):
ix = st.find(substr, start_pos)
if ix == -1:
return accum
return find_all(st, substr, start_pos=ix + 1, accum=accum + [ix])
la compréhension de liste de bstpierre est une bonne solution pour les séquences courtes, mais semble avoir une complexité quadratique et ne jamais se terminer sur un texte long que j'utilisais.
findall_lc = lambda txt, substr: [n for n in xrange(len(txt))
if txt.find(substr, n) == n]
Pour une chaîne aléatoire de longueur non triviale, les deux fonctions donnent le même résultat:
import random, string; random.seed(0)
s = ''.join([random.choice(string.ascii_lowercase) for _ in range(100000)])
>>> find_all(s, 'th') == findall_lc(s, 'th')
True
>>> findall_lc(s, 'th')[:4]
[564, 818, 1872, 2470]
Mais la version quadratique est environ 300 fois plus lente
%timeit find_all(s, 'th')
1000 loops, best of 3: 282 µs per loop
%timeit findall_lc(s, 'th')
10 loops, best of 3: 92.3 ms per loop
Peut-être pas si pythonique, mais un peu plus explicite. Il retourne la position du mot recherché dans la chaîne d'origine.
def retrieve_occurences(sequence, Word, result, base_counter):
indx = sequence.find(Word)
if indx == -1:
return result
result.append(indx + base_counter)
base_counter += indx + len(Word)
return retrieve_occurences(sequence[indx + len(Word):], Word, result, base_counter)
J'avais eu cette idée au hasard il y a peu de temps. L'utilisation d'une boucle While avec le raccordement de chaînes et la recherche de chaînes peut fonctionner même pour les chaînes qui se chevauchent.
findin = "algorithm alma mater alison alternation alpines"
search = "al"
inx = 0
num_str = 0
while True:
inx = findin.find(search)
if inx == -1: #breaks before adding 1 to number of string
break
inx = inx + 1
findin = findin[inx:] #to splice the 'unsearched' part of the string
num_str = num_str + 1 #counts no. of string
if num_str != 0:
print("There are ",num_str," ",search," in your string.")
else:
print("There are no ",search," in your string.")
Je suis un amateur de programmation Python (programmation de n’importe quel langage, en fait) et je ne suis pas sûr des autres problèmes que cela pourrait avoir, mais je suppose que cela fonctionne bien?
Je suppose que lower () pourrait être utilisé quelque part aussi si nécessaire.
Vous pouvez également le faire avec une compréhension de liste conditionnelle comme ceci:
string1= "Allowed Hello Hollow"
string2= "ll"
print [num for num in xrange(len(string1)-len(string2)+1) if string1[num:num+len(string2)]==string2]
# [1, 10, 16]
Je pense qu'il n'est pas nécessaire de tester la longueur du texte; continuez à trouver jusqu'à ce qu'il ne reste plus rien à trouver. Comme ça:
>>> text = 'Allowed Hello Hollow'
>>> place = 0
>>> while text.find('ll', place) != -1:
print('ll found at', text.find('ll', place))
place = text.find('ll', place) + 2
ll found at 1
ll found at 10
ll found at 16
Un code itératif simple qui renvoie une liste d'index où se trouve la sous-chaîne.
def allindices(string, sub):
l=[]
i = string.find(sub)
while i >= 0:
l.append(i)
i = string.find(sub, i + 1)
return l
Vous pouvez diviser pour obtenir des positions relatives, puis additionner des nombres consécutifs dans une liste et ajouter (longueur de chaîne * ordre d’occurrence) en même temps pour obtenir les index de chaînes souhaités.
>>> key = 'll'
>>> text = "Allowed Hello Hollow"
>>> x = [len(i) for i in text.split(key)[:-1]]
>>> [sum(x[:i+1]) + i*len(key) for i in range(len(x))]
[1, 10, 16]
>>>
#!/usr/local/bin python3
#-*- coding: utf-8 -*-
main_string = input()
sub_string = input()
count = counter = 0
for i in range(len(main_string)):
if main_string[i] == sub_string[0]:
k = i + 1
for j in range(1, len(sub_string)):
if k != len(main_string) and main_string[k] == sub_string[j]:
count += 1
k += 1
if count == (len(sub_string) - 1):
counter += 1
count = 0
print(counter)
Ce programme compte le nombre de toutes les sous-chaînes même si elles se chevauchent sans l'utilisation de regex. Mais il s’agit d’une implémentation naïve et pour obtenir de meilleurs résultats dans le pire des cas, il est conseillé d’utiliser Suffix Tree, KMP et d’autres algorithmes et structures de données de correspondance de chaîne.