web-dev-qa-db-fra.com

Recherche de plusieurs occurrences d'une chaîne dans une chaîne en Python

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? 

58
user225312

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.

93
poke

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

23
inspectorG4dget

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]
19
bstpierre

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)
12
intuited

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
3
chauncey
>>> for n,c in enumerate(text):
...   try:
...     if c+text[n+1] == "ll": print n
...   except: pass
...
1
10
16
2
ghostdog74

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)
1
Aaron Semeniuk

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.

0
Abhishek Jebaraj

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)
0
Elias Zamaria

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
0
beardc

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)
0
blasrodri

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.

0

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]
0
Stefan Gruenwald

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
0
rdo

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
0
FReeze FRancis

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]
>>> 
0
WaKo
#!/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.

0
pmsh.93