Come trovare tutte le occorrenze di una sottostringa?


365

Python ha string.find()e string.rfind()per ottenere l'indice di una sottostringa in una stringa.

Mi chiedo se esiste qualcosa di simile string.find_all()che può restituire tutti gli indici trovati (non solo il primo dall'inizio o il primo dalla fine).

Per esempio:

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
cosa dovrebbe 'ttt'.find_all('tt')tornare?
— Santiago Alessandri,

2
dovrebbe restituire '0'. Naturalmente, anche nel mondo perfetto deve esserci 'ttt'.rfind_all('tt'), che dovrebbe restituire '1'
— nukl,

2
Sembra un duplicato di questo stackoverflow.com/questions/3873361/…
— nu everest

Risposte:


523

Non esiste una semplice funzione di stringa incorporata che fa ciò che stai cercando, ma puoi usare le espressioni regolari più potenti :

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

Se vuoi trovare corrispondenze sovrapposte, lookahead lo farà:

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

Se si desidera trovare il contrario senza sovrapposizioni, è possibile combinare lookahead positivo e negativo in un'espressione come questa:

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

re.finditerrestituisce un generatore , quindi è possibile modificare quanto []sopra in ()per ottenere un generatore anziché un elenco che sarà più efficiente se si esegue l'iterazione dei risultati una sola volta.


ciao, riguardo a questo [m.start() for m in re.finditer('test', 'test test test test')], come possiamo cercare testo text? Diventa molto più complicato?
— xpanta,

7
Volete esaminare l'espressione regolare in generale: docs.python.org/2/howto/regex.html . La soluzione alla tua domanda sarà: [m.start () per m in re.finditer ('te [sx] t', 'test test test di testo')]
— Yotam Vaknin

1
Quale sarà la complessità temporale dell'utilizzo di questo metodo?
— Pranjal Mittal,

1
@PranjalMittal. Limite superiore o inferiore? Caso migliore, peggiore o medio?
— Fisico pazzo,

@marcog cosa succede se la sottostringa contiene parentesi o altri caratteri speciali?
— Bananach,

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

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

Quindi, possiamo costruirlo da soli:

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]

Non sono necessarie stringhe o regex temporanee.


22
Per ottenere partite sovrapposte, dovrebbe essere sufficiente sostituirlo start += len(sub)con start += 1.
— Karl Knechtel,

4
Credo che il tuo commento precedente dovrebbe essere un poscritto nella tua risposta.
— tzot

1
Il tuo codice non funziona per trovare il substr: "ATAT" in "GATATATGCATATACTT"
— Ashish Negi,

2
Vedi il commento che ho fatto in aggiunta. Questo è un esempio di una partita sovrapposta.
— Karl Knechtel,

4
Per abbinare il comportamento di re.findall, consiglierei di aggiungere len(sub) or 1invece di len(sub), altrimenti questo generatore non terminerà mai su sottostringa vuota.
— WGH,

45

Ecco un modo (molto inefficiente) per ottenere tutte le corrispondenze (cioè anche la sovrapposizione):

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

25

Ancora una volta, vecchio thread, ma ecco la mia soluzione usando un generatore e semplice 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)

Esempio

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

ritorna

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

3
sembra bellissimo!
— fabio.sang

21

Puoi usarlo re.finditer()per le partite non sovrapposte.

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

ma non funzionerà per:

In [1]: aString="ababa"

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

12
Perché creare un elenco da un iteratore, rallenta semplicemente il processo.
— pradyunsg,

2
aString VS astring;)
— NexD.

18

Vieni, cerchiamo di ricorrere insieme.

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]

Non sono necessarie espressioni regolari in questo modo.


Ho appena iniziato a chiedermi "esiste un modo elegante per individuare una sottostringa all'interno di una stringa in pitone" ... e poi dopo 5 minuti di ricerca su google ho trovato il tuo codice. Grazie per la condivisione!!!
— Geparada,

3
Questo codice ha diversi problemi. Dal momento che sta lavorando su dati aperti prima o poi ti imbatterai in RecursionErrorse ci sono abbastanza eventi. Un altro sono due elenchi usa e getta che crea su ogni iterazione solo per aggiungere un elemento, che è molto subottimale per una funzione di ricerca di stringhe, che potrebbe essere chiamata molte volte. Sebbene a volte le funzioni ricorsive sembrino eleganti e chiare, dovrebbero essere prese con cautela.
— Ivan Nikolaev,

11

Se stai solo cercando un singolo personaggio, questo funzionerebbe:

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

Anche,

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

La mia impressione è che nessuno di questi (specialmente il n. 2) sia terribilmente performante.


soluzione gr8 .. sono impressionato dall'uso di .. split ()
— shantanu pathak

9

questo è un vecchio thread ma mi sono interessato e volevo condividere la mia soluzione.

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

Dovrebbe restituire un elenco di posizioni in cui è stata trovata la sottostringa. Commenta se vedi un errore o spazio per l'improvvisazione.


6

Questo è il trucco per me 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

Questo thread è un po 'vecchio ma ha funzionato per me:

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

Puoi provare :

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

Qualunque sia la soluzione fornita da altri, si basa completamente sul metodo disponibile find () o su eventuali metodi disponibili.

Qual è l'algoritmo di base principale per trovare tutte le occorrenze di una sottostringa in una stringa?

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

È inoltre possibile ereditare la classe str in una nuova classe e utilizzare questa funzione di seguito.

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

Chiamando il metodo

newstr.find_all ('Trovi utile questa risposta? quindi vota questo!', 'questo')


2

Questa funzione non esamina tutte le posizioni all'interno della stringa, non spreca risorse di calcolo. Il mio tentativo:

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

per usarlo chiamalo così:

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

1

Quando cerchi una grande quantità di parole chiave in un documento, usa 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 funziona più velocemente di regex in un ampio elenco di parole di ricerca.


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
Sebbene questo codice possa risolvere il problema del PO, è meglio includere una spiegazione su come il codice risolve il problema del PO. In questo modo, i futuri visitatori possono imparare dal tuo post e applicarlo al proprio codice. SO non è un servizio di codifica, ma una risorsa per la conoscenza. Inoltre, è più probabile che vengano votate risposte complete e di alta qualità. Queste caratteristiche, insieme al requisito secondo cui tutti i post sono autonomi, sono alcuni dei punti di forza di SO come piattaforma, che lo differenzia dai forum. Puoi modificare per aggiungere ulteriori informazioni e / o integrare le tue spiegazioni con la documentazione di origine
— SherylHohman,

0

Questa è la soluzione di una domanda simile da hackerrank. Spero che questo possa aiutarti.

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

Produzione:

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

-1

Sezioniamo troviamo tutte le combinazioni possibili e le accodiamo in un elenco e troviamo il numero di volte che si verifica usando la countfunzione

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

Quando s="test test test test"e f="test"il codice viene stampato 4, ma OP previsto[0,5,10,15]
— barbsan

Scritto per una sola parola aggiornerà il codice
— BONTHA SREEVIDHYA

-2

si prega di guardare sotto il codice

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

Il modo pitonico sarebbe:

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) In che modo aiuta una domanda a cui è stata data risposta 7 anni fa? 2) L' uso in lambdaquesto modo non è Pythonic e va contro PEP8 . 3) Questo non fornisce l'output corretto per la situazione dei PO
— Wondercricket,

Pythonic non significa "Usa tutte le funzionalità di Python che puoi pensare"
— klutt

-2

Puoi usare facilmente:

string.count('test')!

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

Saluti!


questa dovrebbe essere la risposta
— Maxwell Chandler del

8
Il metodo string count () restituisce il numero di occorrenze di una sottostringa nella stringa specificata. Non la loro posizione.
— Astrid,

5
questo non soddisfa tutti i casi, s = 'banana', sub = 'ana'. Sub si verifica in questa situazione due volte ma fare s.sub ('ana') restituirebbe 1
— Joey daniel darko,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.