Como encontrar todas as ocorrências de uma substring?

365

Python possui string.find()e string.rfind()obtém o índice de uma substring em uma string.

Gostaria de saber se existe algo como o string.find_all()que pode retornar todos os índices encontrados (não apenas o primeiro desde o início ou o primeiro a partir do final).

Por exemplo:

string = "test test test test"

print string.find('test') # 0
print string.rfind('test') # 15

#this is the goal
print string.find_all('test') # [0,5,10,15]
nukl
fonte
11
o que deve 'ttt'.find_all('tt')retornar?
Santiago Alessandri
2
deve retornar '0'. Obviamente, no mundo perfeito também deve haver 'ttt'.rfind_all('tt'), o que deve retornar '1'
nukl
2
Parece uma duplicata deste stackoverflow.com/questions/3873361/…
nu everest

Respostas:

523

Não existe uma função de cadeia interna simples que faça o que você está procurando, mas você pode usar as expressões regulares mais poderosas :

import re
[m.start() for m in re.finditer('test', 'test test test test')]
#[0, 5, 10, 15]

Se você deseja encontrar correspondências sobrepostas, o lookahead fará isso:

[m.start() for m in re.finditer('(?=tt)', 'ttt')]
#[0, 1]

Se você deseja encontrar tudo inversamente sem sobreposições, pode combinar um lookahead positivo e negativo em uma expressão como esta:

search = 'tt'
[m.start() for m in re.finditer('(?=%s)(?!.{1,%d}%s)' % (search, len(search)-1, search), 'ttt')]
#[1]

re.finditerretorna um gerador , para que você possa alterar o []item acima para ()obter um gerador em vez de uma lista que será mais eficiente se você estiver apenas repetindo os resultados uma vez.

moinudin
fonte
oi, sobre isso [m.start() for m in re.finditer('test', 'test test test test')], como podemos procurar testou text? Torna-se muito mais complicado?
Xpanta
7
Você deseja examinar a expressão regular em geral: docs.python.org/2/howto/regex.html . A solução para sua pergunta será: [m.start () para m no re.finditer ('te [sx] t', 'teste de texto teste de texto')]
Yotam Vaknin
11
Qual será a complexidade temporal do uso desse método?
Pranjal Mittal
11
@PranjalMittal. Limite superior ou inferior? Melhor, pior ou caso médio?
Mad Físico
@marcog e se a substring contiver parênteses ou outros caracteres especiais?
Bananach 10/11/19
109
>>> help(str.find)
Help on method_descriptor:

find(...)
    S.find(sub [,start [,end]]) -> int

Assim, nós podemos construí-lo:

def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1: return
        yield start
        start += len(sub) # use start += 1 to find overlapping matches

list(find_all('spam spam spam spam', 'spam')) # [0, 5, 10, 15]

Não são necessárias seqüências de caracteres ou expressões regulares temporárias.

Karl Knechtel
fonte
22
Para obter correspondências sobrepostas, basta substituir start += len(sub)por start += 1.
Karl Knechtel
4
Acredito que seu comentário anterior deve ser um postscript em sua resposta.
tzot 6/02/11
11
Seu código não funciona para localizar substr: "ATAT" em "GATATATGCATATACTT"
Ashish Negi
2
Veja o comentário que fiz em adição. Esse é um exemplo de uma correspondência sobreposta.
19413 Karl Knechtel
4
Para corresponder ao comportamento de re.findall, eu recomendo adicionar em len(sub) or 1vez de len(sub), caso contrário, esse gerador nunca será encerrado em substring vazio.
WGH 27/11/2015
45

Aqui está uma maneira (muito ineficiente) de obter todas as correspondências (ou seja, sobrepostas):

>>> string = "test test test test"
>>> [i for i in range(len(string)) if string.startswith('test', i)]
[0, 5, 10, 15]
thkala
fonte
25

Novamente, thread antigo, mas aqui está a minha solução usando um gerador e simples str.find.

def findall(p, s):
    '''Yields all the positions of
    the pattern p in the string s.'''
    i = s.find(p)
    while i != -1:
        yield i
        i = s.find(p, i+1)

Exemplo

x = 'banananassantana'
[(i, x[i:i+2]) for i in findall('na', x)]

retorna

[(2, 'na'), (4, 'na'), (6, 'na'), (14, 'na')]
AkiRoss
fonte
3
isso está lindo!
Fabio.sang 28/03/19
21

Você pode usar re.finditer()para correspondências sem sobreposição.

>>> import re
>>> aString = 'this is a string where the substring "is" is repeated several times'
>>> print [(a.start(), a.end()) for a in list(re.finditer('is', aString))]
[(2, 4), (5, 7), (38, 40), (42, 44)]

mas não funcionará para:

In [1]: aString="ababa"

In [2]: print [(a.start(), a.end()) for a in list(re.finditer('aba', aString))]
Output: [(0, 3)]
Chinmay Kanchi
fonte
12
Por que fazer uma lista de um iterador, isso apenas atrasa o processo.
Pradyunsg
2
aString VS adstringente;)
NexD.
18

Venha, vamos recuar juntos.

def locations_of_substring(string, substring):
    """Return a list of locations of a substring."""

    substring_length = len(substring)    
    def recurse(locations_found, start):
        location = string.find(substring, start)
        if location != -1:
            return recurse(locations_found + [location], location+substring_length)
        else:
            return locations_found

    return recurse([], 0)

print(locations_of_substring('this is a test for finding this and this', 'this'))
# prints [0, 27, 36]

Não há necessidade de expressões regulares dessa maneira.

Cody Piersall
fonte
Eu apenas comecei a pensar "existe uma maneira elegante de localizar uma substring dentro de uma string em python" ... e depois de 5 minutos de pesquisa, encontrei seu código. Obrigado por compartilhar !!!
Geparada
3
Este código tem vários problemas. Como está trabalhando com dados abertos mais cedo ou mais tarde, você RecursionErrorencontrará se houver muitas ocorrências suficientes. Outra são duas listas descartáveis ​​que ele cria em cada iteração apenas para acrescentar um elemento, o que é muito abaixo do ideal para uma função de localização de string, que pode ser chamada várias vezes. Embora algumas vezes as funções recursivas pareçam elegantes e claras, elas devem ser tomadas com cautela.
Ivan Nikolaev
11

Se você está procurando apenas um personagem, isso funcionaria:

string = "dooobiedoobiedoobie"
match = 'o'
reduce(lambda count, char: count + 1 if char == match else count, string, 0)
# produces 7

Além disso,

string = "test test test test"
match = "test"
len(string.split(match)) - 1
# produces 4

Meu palpite é que nenhum deles (especialmente o número 2) tem um desempenho terrível.

jstaab
fonte
solução gr8 .. Estou impressionado com uso de .. split ()
shantanu pathak
9

este é um tópico antigo, mas fiquei interessado e queria compartilhar minha solução.

def find_all(a_string, sub):
    result = []
    k = 0
    while k < len(a_string):
        k = a_string.find(sub, k)
        if k == -1:
            return result
        else:
            result.append(k)
            k += 1 #change to k += len(sub) to not search overlapping results
    return result

Ele deve retornar uma lista de posições em que a substring foi encontrada. Comente se você encontrar um erro ou espaço para melhorias.

Thurines
fonte
6

Isso faz o truque para mim usando o re.finditer

import re

text = 'This is sample text to test if this pythonic '\
       'program can serve as an indexing platform for '\
       'finding words in a paragraph. It can give '\
       'values as to where the word is located with the '\
       'different examples as stated'

#  find all occurances of the word 'as' in the above text

find_the_word = re.finditer('as', text)

for match in find_the_word:
    print('start {}, end {}, search string \'{}\''.
          format(match.start(), match.end(), match.group()))
Bruno Vermeulen
fonte
5

Este tópico é um pouco antigo, mas funcionou para mim:

numberString = "onetwothreefourfivesixseveneightninefiveten"
testString = "five"

marker = 0
while marker < len(numberString):
    try:
        print(numberString.index("five",marker))
        marker = numberString.index("five", marker) + 1
    except ValueError:
        print("String not found")
        marker = len(numberString)
Andrew H
fonte
5

Podes tentar :

>>> string = "test test test test"
>>> for index,value in enumerate(string):
    if string[index:index+(len("test"))] == "test":
        print index

0
5
10
15
Harsha Biyani
fonte
2

Quaisquer que sejam as soluções fornecidas por outras pessoas, são completamente baseadas no método disponível find () ou em qualquer método disponível.

Qual é o algoritmo básico do núcleo para encontrar todas as ocorrências de uma substring em uma string?

def find_all(string,substring):
    """
    Function: Returning all the index of substring in a string
    Arguments: String and the search string
    Return:Returning a list
    """
    length = len(substring)
    c=0
    indexes = []
    while c < len(string):
        if string[c:c+length] == substring:
            indexes.append(c)
        c=c+1
    return indexes

Você também pode herdar a classe str para a nova classe e pode usar esta função abaixo.

class newstr(str):
def find_all(string,substring):
    """
    Function: Returning all the index of substring in a string
    Arguments: String and the search string
    Return:Returning a list
    """
    length = len(substring)
    c=0
    indexes = []
    while c < len(string):
        if string[c:c+length] == substring:
            indexes.append(c)
        c=c+1
    return indexes

Chamando o método

newstr.find_all ('Você achou esta resposta útil? então vote aqui!', 'this')

naveen raja
fonte
2

Esta função não olha para todas as posições dentro da string, não desperdiça recursos de computação. Minha tentativa:

def findAll(string,word):
    all_positions=[]
    next_pos=-1
    while True:
        next_pos=string.find(word,next_pos+1)
        if(next_pos<0):
            break
        all_positions.append(next_pos)
    return all_positions

para usá-lo, chame assim:

result=findAll('this word is a big word man how many words are there?','word')
Valentin Goikhman
fonte
1

Ao procurar uma grande quantidade de palavras-chave em um documento, use flashtext

from flashtext import KeywordProcessor
words = ['test', 'exam', 'quiz']
txt = 'this is a test'
kwp = KeywordProcessor()
kwp.add_keywords_from_list(words)
result = kwp.extract_keywords(txt, span_info=True)

O Flashtext roda mais rápido que o regex na grande lista de palavras de pesquisa.

Uri Goren
fonte
0
src = input() # we will find substring in this string
sub = input() # substring

res = []
pos = src.find(sub)
while pos != -1:
    res.append(pos)
    pos = src.find(sub, pos + 1)
mascai
fonte
11
Embora esse código possa resolver o problema do OP, é melhor incluir uma explicação sobre como o seu código soluciona o problema do OP. Dessa forma, futuros visitantes podem aprender com sua postagem e aplicá-la ao próprio código. O SO não é um serviço de codificação, mas um recurso para o conhecimento. Além disso, respostas completas de alta qualidade têm mais probabilidade de serem votadas. Esses recursos, juntamente com o requisito de que todas as postagens sejam independentes, são alguns dos pontos fortes do SO como plataforma, que o diferencia dos fóruns. Você pode editar para adicionar informações adicionais e / ou complementar suas explicações com a documentação de origem
SherylHohman
0

Esta é a solução de uma pergunta semelhante do hackerrank. Espero que isso possa ajudá-lo.

import re
a = input()
b = input()
if b not in a:
    print((-1,-1))
else:
    #create two list as
    start_indc = [m.start() for m in re.finditer('(?=' + b + ')', a)]
    for i in range(len(start_indc)):
        print((start_indc[i], start_indc[i]+len(b)-1))

Resultado:

aaadaa
aa
(0, 1)
(1, 2)
(4, 5)
Ruman Khan
fonte
-1

Ao fatiar, encontramos todas as combinações possíveis e as anexamos a uma lista e encontramos o número de vezes que ocorre usando a countfunção

s=input()
n=len(s)
l=[]
f=input()
print(s[0])
for i in range(0,n):
    for j in range(1,n+1):
        l.append(s[i:j])
if f in l:
    print(l.count(f))
BONTHA SREEVIDHYA
fonte
Quando s="test test test test"e f="test"suas impressões de código 4, mas OP esperado[0,5,10,15]
barbsan
Ter escrito para uma única palavra irá atualizar o código
BONTHA SREEVIDHYA
-2

por favor, olhe o código abaixo

#!/usr/bin/env python
# coding:utf-8
'''黄哥Python'''


def get_substring_indices(text, s):
    result = [i for i in range(len(text)) if text.startswith(s, i)]
    return result


if __name__ == '__main__':
    text = "How much wood would a wood chuck chuck if a wood chuck could chuck wood?"
    s = 'wood'
    print get_substring_indices(text, s)
(Python)
fonte
-2

O caminho pitônico seria:

mystring = 'Hello World, this should work!'
find_all = lambda c,s: [x for x in range(c.find(s), len(c)) if c[x] == s]

# s represents the search string
# c represents the character string

find_all(mystring,'o')    # will return all positions of 'o'

[4, 7, 20, 26] 
>>> 
Harvey
fonte
3
1) Como isso ajuda em uma pergunta que foi respondida há 7 anos? 2) O uso lambdadessa maneira não é Pythonic e vai contra o PEP8 . 3) Esta não fornece a saída correta para a situação PO
Wondercricket
Pythonic não significa "Use tantos recursos de python quanto você possa imaginar"
klutt 03/06
-2

Você pode usar facilmente:

string.count('test')!

https://www.programiz.com/python-programming/methods/string/count

Felicidades!

RaySaraiva
fonte
esta deve ser a resposta
Maxwell Chandler
8
O método string () retorna o número de ocorrências de uma substring na string especificada. Não é a localização deles.
Astrid
5
isso não satisfaz todos os casos, s = 'banana', sub = 'ana'. Sub ocorre nessa situação duas vezes, mas fazer s.sub ('ana') retornaria 1
Joey daniel darko