¿Cómo encontrar todas las ocurrencias de una subcadena?


365

Python tiene string.find()ystring.rfind() para obtener el índice de una subcadena en una cadena.

Me pregunto si hay algo como lo string.find_all()que puede devolver todos los índices encontrados (no solo el primero desde el principio o el primero desde el final).

Por ejemplo:

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]

11
¿Qué debería 'ttt'.find_all('tt')volver?
Santiago Alessandri

2
debería devolver '0'. Por supuesto, en el mundo perfecto también tiene que haber 'ttt'.rfind_all('tt'), que debería devolver '1'
nukl

2
Parece un duplicado de este stackoverflow.com/questions/3873361/…
nu everest

Respuestas:


523

No existe una función de cadena integrada simple que haga lo que está buscando, pero podría usar las expresiones regulares más potentes :

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

Si desea encontrar coincidencias superpuestas, lookahead lo hará:

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

Si desea una búsqueda inversa sin superposiciones, puede combinar anticipación positiva y negativa en una expresión como esta:

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

re.finditerdevuelve un generador , por lo que podría cambiar lo []anterior para ()obtener un generador en lugar de una lista que será más eficiente si solo está iterando a través de los resultados una vez.


hola, con respecto a esto [m.start() for m in re.finditer('test', 'test test test test')], ¿cómo podemos buscar testo text? ¿Se vuelve mucho más complicado?
xpanta

77
Desea buscar expresiones regulares en general: docs.python.org/2/howto/regex.html . La solución a su pregunta será: [m.start () para m en re.finditer ('te [sx] t', 'text test text test')]
Yotam Vaknin

1
¿Cuál será la complejidad temporal del uso de este método?
Pranjal Mittal

1
@PranjalMittal. Límite superior o inferior? ¿El mejor, el peor o el caso promedio?
Físico loco

@marcog, ¿qué pasa si la subcadena contiene paréntesis u otros caracteres especiales?
Bananach

109
>>> help(str.find)
Help on method_descriptor:

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

Por lo tanto, podemos construirlo nosotros mismos:

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]

No se requieren cadenas temporales o expresiones regulares.


22
Para obtener coincidencias superpuestas, debería ser suficiente reemplazar start += len(sub)con start += 1.
Karl Knechtel el

44
Creo que su comentario anterior debería ser una posdata en su respuesta.
tzot

1
Su código no funciona para encontrar substr: "ATAT" en "GATATATGCATATACTT"
Ashish Negi

2
Vea el comentario que hice además. Ese es un ejemplo de una coincidencia superpuesta.
Karl Knechtel

44
Para que coincida con el comportamiento de re.findall, recomendaría agregar en len(sub) or 1lugar de len(sub), de lo contrario, este generador nunca terminará en una subcadena vacía.
WGH

45

Aquí hay una forma (muy ineficiente) de obtener todas las coincidencias (es decir, incluso superpuestas):

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

25

Una vez más, viejo hilo, pero aquí está mi solución usando un generador y simple 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)

Ejemplo

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

devoluciones

[(2, 'na'), (4, 'na'), (6, 'na'), (14, 'na')]

3
esto se ve hermoso!
fabio.sang

21

Puede usar re.finditer()para coincidencias no superpuestas.

>>> 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)]

pero no funcionará para:

In [1]: aString="ababa"

In [2]: print [(a.start(), a.end()) for a in list(re.finditer('aba', aString))]
Output: [(0, 3)]

12
¿Por qué hacer una lista de un iterador? Simplemente ralentiza el proceso.
pradyunsg

2
aString VS astring;)
NexD.

18

Ven, volvamos 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]

No hay necesidad de expresiones regulares de esta manera.


Acabo de empezar a preguntarme "¿hay alguna manera elegante de localizar una subcadena dentro de una cadena en Python" ... y luego, después de 5 minutos de búsqueda en Google, encontré su código. ¡¡¡Gracias por compartir!!!
Geparada

3
Este código tiene varios problemas. Ya que está trabajando en datos abiertos tarde o temprano, te toparás RecursionErrorsi hay muchos eventos suficientes. Otra son dos listas de descarte que crea en cada iteración solo por agregar un elemento, que es muy subóptimo para una función de búsqueda de cadenas, que posiblemente podría llamarse muchas veces. Aunque a veces las funciones recursivas parecen elegantes y claras, deben tomarse con precaución.
Ivan Nikolaev

11

Si solo está buscando un solo personaje, esto funcionaría:

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

También,

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

Mi presentimiento es que ninguno de estos (especialmente # 2) es terriblemente eficaz.


solución gr8 ... estoy impresionado con el uso de ... split ()
shantanu pathak

9

Este es un hilo viejo pero me interesé y quería compartir mi solución.

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

Debería devolver una lista de posiciones donde se encontró la subcadena. Comente si ve un error o un margen de mejora.


6

Esto hace el truco para mí usando 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()))

5

Este hilo es un poco viejo pero esto funcionó para mí:

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)

5

Puedes probar :

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

0
5
10
15

2

Cualesquiera que sean las soluciones proporcionadas por otros, se basan completamente en el método disponible find () o en cualquier método disponible.

¿Cuál es el algoritmo básico básico para encontrar todas las ocurrencias de una subcadena en una cadena?

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

También puede heredar la clase str a una nueva clase y puede usar esta función a continuación.

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

Llamando al método

newstr.find_all ('¿Le resulta útil esta respuesta? ¡luego vote por esto!', 'esto')


2

Esta función no mira todas las posiciones dentro de la cadena, no desperdicia recursos de cómputo. Mi intento:

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 usarlo llámalo así:

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

1

Cuando busque una gran cantidad de palabras clave en un 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)

Flashtext se ejecuta más rápido que regex en una gran lista de palabras de búsqueda.


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)

1
Si bien este código puede resolver el problema del OP, es mejor incluir una explicación sobre cómo su código aborda el problema del OP. De esta manera, los futuros visitantes pueden aprender de su publicación y aplicarla a su propio código. SO no es un servicio de codificación, sino un recurso para el conocimiento. Además, las respuestas completas de alta calidad tienen más probabilidades de ser votadas. Estas características, junto con el requisito de que todas las publicaciones sean independientes, son algunos de los puntos fuertes de SO como plataforma, que lo diferencia de los foros. Puede editar para agregar información adicional y / o para complementar sus explicaciones con la documentación original
SherylHohman

0

Esta es la solución de una pregunta similar de hackerrank. Espero que esto te pueda ayudar.

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

Salida:

aaadaa
aa
(0, 1)
(1, 2)
(4, 5)

-1

Al dividir, encontramos todas las combinaciones posibles y las agregamos en una lista y encontramos el número de veces que ocurre usando la countfunción

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

Cuándo s="test test test test"y f="test"su código se imprime 4, pero OP esperado[0,5,10,15]
barbsan

Haber escrito para una sola palabra actualizará el código
BONTHA SREEVIDHYA

-2

mira el siguiente código

#!/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)

-2

La forma pitónica sería:

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

3
1) ¿Cómo ayuda esto a una pregunta que fue respondida hace 7 años? 2) El uso de lambdaesta manera no es Pythonic y va en contra de PEP8 . 3) Esto no proporciona la salida correcta para la situación de los OP
Wondercricket

Pythonic no significa "Usar tantas funciones de Python como se te
ocurran

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.