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.