Wie finde ich alle Vorkommen eines Teilstrings?

365

Python hat string.find()und string.rfind()um den Index eines Teilstrings in einem String zu erhalten.

Ich frage mich, ob es so etwas gibt, string.find_all()das alle gefundenen Indizes zurückgeben kann (nicht nur den ersten vom Anfang oder den ersten vom Ende).

Zum Beispiel:

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
quelle
11
Was soll 'ttt'.find_all('tt')zurückkehren?
Santiago Alessandri
2
es sollte '0' zurückgeben. Natürlich muss es in der perfekten Welt auch solche geben 'ttt'.rfind_all('tt'), die '1' zurückgeben sollten
nukl
2
Scheint wie ein Duplikat dieses stackoverflow.com/questions/3873361/…
nu everest

Antworten:

523

Es gibt keine einfache integrierte Zeichenfolgenfunktion, die das tut, wonach Sie suchen, aber Sie könnten die leistungsstärkeren regulären Ausdrücke verwenden :

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

Wenn Sie überlappende Übereinstimmungen suchen möchten, führt Lookahead Folgendes aus :

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

Wenn Sie eine umgekehrte Suche ohne Überlappungen wünschen, können Sie positive und negative Lookahead zu einem Ausdruck wie diesem kombinieren:

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

re.finditerGibt einen Generator zurück , sodass Sie []die obigen Angaben ändern können (), um einen Generator anstelle einer Liste zu erhalten, die effizienter ist, wenn Sie die Ergebnisse nur einmal durchlaufen.

Moinudin
quelle
Hallo, [m.start() for m in re.finditer('test', 'test test test test')]wie können wir danach suchen testoder text? Wird es viel komplizierter?
Xpanta
7
Sie möchten sich allgemein mit regulären Ausdrücken befassen : docs.python.org/2/howto/regex.html . Die Lösung für Ihre Frage lautet: [m.start () für m in re.finditer ('te [sx] t', 'Texttest Texttest')]
Yotam Vaknin
1
Wie zeitlich komplex wird diese Methode sein?
Pranjal Mittal
1
@PranjalMittal. Ober- oder Untergrenze? Bester, schlechtester oder durchschnittlicher Fall?
Mad Physicist
@marcog was ist, wenn der Teilstring Klammern oder andere Sonderzeichen enthält?
Bananach
109
>>> help(str.find)
Help on method_descriptor:

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

So können wir es selbst bauen:

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]

Keine temporären Zeichenfolgen oder regulären Ausdrücke erforderlich.

Karl Knechtel
quelle
22
Um überlappende Streichhölzer, sollte es genügen , zu ersetzen start += len(sub)mit start += 1.
Karl Knechtel
4
Ich glaube, Ihr vorheriger Kommentar sollte ein Postskriptum in Ihrer Antwort sein.
Zot
1
Ihr Code funktioniert nicht zum Finden von substr: "ATAT" in "GATATATGCATATACTT"
Ashish Negi
2
Siehe den Kommentar, den ich zusätzlich gemacht habe. Das ist ein Beispiel für eine überlappende Übereinstimmung.
Karl Knechtel
4
Um dem Verhalten von zu entsprechen re.findall, würde ich empfehlen, len(sub) or 1anstelle von hinzuzufügen len(sub), da dieser Generator sonst niemals bei leerem Teilstring endet.
WGH
45

Hier ist ein (sehr ineffizienter) Weg, um alle (dh sogar überlappenden) Übereinstimmungen zu erhalten:

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

Wieder alter Thread, aber hier ist meine Lösung mit einem Generator und Plain 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)

Beispiel

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

kehrt zurück

[(2, 'na'), (4, 'na'), (6, 'na'), (14, 'na')]
AkiRoss
quelle
3
das sieht wunderschön aus!
fabio.sang
21

Sie können re.finditer()für nicht überlappende Übereinstimmungen verwenden.

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

funktioniert aber nicht für:

In [1]: aString="ababa"

In [2]: print [(a.start(), a.end()) for a in list(re.finditer('aba', aString))]
Output: [(0, 3)]
Chinmay Kanchi
quelle
12
Warum aus einem Iterator eine Liste erstellen, verlangsamt dies nur den Prozess.
Pradyunsg
2
aString VS astring;)
NexD.
18

Komm, lass uns zusammen zurückkehren.

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]

Auf diese Weise sind keine regulären Ausdrücke erforderlich.

Cody Piersall
quelle
Ich habe mich gerade gefragt, ob es eine ausgefallene Möglichkeit gibt, einen Teilstring in einem String in Python zu finden. Nach fünf Minuten Googeln habe ich dann Ihren Code gefunden. Danke für das Teilen!!!
Geparada
3
Dieser Code hat mehrere Probleme. Da es früher oder später mit offenen Daten arbeitet, werden Sie darauf stoßen, RecursionErrorwenn es genügend Vorkommen gibt. Eine andere sind zwei Wegwerflisten, die bei jeder Iteration erstellt werden, nur um ein Element anzuhängen, was für eine Zeichenfolgenfindungsfunktion, die möglicherweise häufig aufgerufen werden kann, sehr suboptimal ist. Obwohl rekursive Funktionen manchmal elegant und klar erscheinen, sollten sie mit Vorsicht betrachtet werden.
Ivan Nikolaev
11

Wenn Sie nur nach einem einzelnen Charakter suchen, würde dies funktionieren:

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

Ebenfalls,

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

Meine Vermutung ist, dass keines von diesen (besonders # 2) schrecklich performant ist.

jstaab
quelle
gr8 Lösung .. Ich bin beeindruckt von der Verwendung von .. split ()
Shantanu Pathak
9

Dies ist ein alter Thread, aber ich wurde interessiert und wollte meine Lösung teilen.

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

Es sollte eine Liste der Positionen zurückgeben, an denen der Teilstring gefunden wurde. Bitte kommentieren Sie, wenn Sie einen Fehler oder Verbesserungsbedarf sehen.

Thurines
quelle
6

Dies macht den Trick für mich mit 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
quelle
5

Dieser Thread ist etwas alt, aber das hat bei mir funktioniert:

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.
quelle
5

Du kannst es versuchen :

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

Unabhängig davon, welche Lösungen von anderen bereitgestellt werden, basieren sie vollständig auf der verfügbaren Methode find () oder verfügbaren Methoden.

Was ist der grundlegende Grundalgorithmus, um alle Vorkommen eines Teilstrings in einem String zu finden?

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

Sie können die Klasse str auch an eine neue Klasse erben und diese Funktion unten verwenden.

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

Methode aufrufen

newstr.find_all ('Findest du diese Antwort hilfreich? Dann stimme dem zu!', 'this')

Naveen Raja
quelle
2

Diese Funktion betrachtet nicht alle Positionen innerhalb der Zeichenfolge und verschwendet keine Rechenressourcen. Mein Versuch:

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

um es zu benutzen, nenne es so:

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

Verwenden Sie Flashtext, wenn Sie nach einer großen Anzahl von Schlüsselwörtern in einem Dokument suchen

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 wird in einer großen Liste von Suchwörtern schneller ausgeführt als Regex.

Uri Goren
quelle
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
quelle
1
Während dieser Code das Problem des OP lösen kann, ist es am besten, eine Erklärung beizufügen, wie Ihr Code das Problem des OP behebt. Auf diese Weise können zukünftige Besucher aus Ihrem Beitrag lernen und ihn auf ihren eigenen Code anwenden. SO ist kein Codierungsdienst, sondern eine Ressource für Wissen. Es ist auch wahrscheinlicher, dass qualitativ hochwertige, vollständige Antworten positiv bewertet werden. Diese Funktionen sowie die Anforderung, dass alle Beiträge in sich geschlossen sind, sind einige der Stärken von SO als Plattform, die es von Foren unterscheidet. Sie können bearbeiten, um zusätzliche Informationen hinzuzufügen und / oder Ihre Erklärungen durch Quelldokumentation zu ergänzen
SherylHohman
0

Dies ist die Lösung einer ähnlichen Frage von Hackerrank. Ich hoffe das könnte dir helfen.

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

Ausgabe:

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

Durch Schneiden finden wir alle möglichen Kombinationen und fügen sie in eine Liste ein und ermitteln, wie oft sie mit der countFunktion auftreten

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
quelle
Wann s="test test test test"und f="test"Ihr Code wird gedruckt 4, aber OP erwartet[0,5,10,15]
Barbansan
Haben für ein einziges Wort geschrieben, wird der Code aktualisiert
BONTHA SREEVIDHYA
-2

Bitte schauen Sie sich den folgenden Code an

#!/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 培训
quelle
-2

Der pythonische Weg wäre:

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
quelle
3
1) Wie hilft dies einer Frage, die vor 7 Jahren beantwortet wurde? 2) Die Verwendung lambdadieses Weges ist nicht pythonisch und widerspricht PEP8 . 3) Dies liefert nicht die richtige Ausgabe für die OP-Situation
Wondercricket
Pythonic bedeutet nicht "Verwenden Sie so viele Funktionen von Python, wie Sie sich
vorstellen
-2

Sie können leicht verwenden:

string.count('test')!

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

Prost!

RaySaraiva
quelle
Dies sollte die Antwort sein
Maxwell Chandler
8
Die Methode string count () gibt die Anzahl der Vorkommen eines Teilstrings in der angegebenen Zeichenfolge zurück. Nicht ihr Standort.
Astrid
5
Dies erfüllt nicht alle Fälle, s = 'Banane', sub = 'Ana'. Sub kommt in dieser Situation zweimal vor, aber wenn man s ('ana') macht, wird 1
Joey Daniel Darko