Как найти все вхождения подстроки?


366

Python имеет string.find()и string.rfind()получить индекс подстроки в строке.

Мне интересно, есть ли что-то подобное, string.find_all()которое может вернуть все найденные индексы (не только первый с начала или первый с конца).

Например:

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
что должно 'ttt'.find_all('tt')вернуться?
Сантьяго Алессандри

2
он должен вернуть «0». Конечно, в идеальном мире тоже должно быть 'ttt'.rfind_all('tt'), что должно возвращать «1»
нукл

2
Похоже, дубликат этого stackoverflow.com/questions/3873361/…
nu everest

Ответы:


523

Нет простой встроенной строковой функции, которая делает то, что вы ищете, но вы можете использовать более мощные регулярные выражения :

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

Если вы хотите найти совпадающие совпадения, Lookahead сделает это:

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

Если вам нужен обратный поиск всех без наложений, вы можете объединить положительный и отрицательный взгляд в следующее выражение:

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

re.finditerвозвращает генератор , так что вы могли бы изменить []в приведенном выше, чтобы ()получить генератор вместо списка, который будет более эффективным, если вы только итерируете результаты один раз.


привет, относительно этого [m.start() for m in re.finditer('test', 'test test test test')], как мы можем искать testили text? Становится ли это намного сложнее?
xpanta

7
Вы хотите посмотреть на регулярные выражения в целом: docs.python.org/2/howto/regex.html . Решением вашего вопроса будет: [m.start () для m в re.finditer ('te [sx] t', 'text test text test')]
Йотам Вакнин

1
Какова будет временная сложность использования этого метода?
Пранджал Миттал

1
@PranjalMittal. Верхняя или нижняя граница? Лучший, худший или средний случай?
Безумный физик

@marcog, что если подстрока содержит скобки или другие специальные символы?
Bananach

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

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

Таким образом, мы можем построить это сами:

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]

Никаких временных строк или регулярных выражений не требуется.


22
Чтобы получить совпадающие совпадения, достаточно заменить start += len(sub)на start += 1.
Карл Кнехтель

4
Я считаю, что ваш предыдущий комментарий должен быть постскриптумом в вашем ответе.
tzot

1
Ваш код не работает для поиска substr: "ATAT" в "GATATATGCATATACTT"
Ашиш Неги

2
Смотрите комментарий, который я сделал в дополнение. Это пример совпадения.
Карл Кнехтель,

4
Чтобы соответствовать поведению re.findall, я бы рекомендовал добавить len(sub) or 1вместо len(sub), иначе этот генератор никогда не завершится на пустой подстроке.
WGH

45

Вот (очень неэффективный) способ получить все (то есть даже перекрывающиеся) совпадения:

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

25

Опять же, старый поток, но вот мое решение с использованием генератора и простой 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)

пример

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

возвращается

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

3
это выглядит красиво!
fabio.sang

21

Вы можете использовать re.finditer()для неперекрывающихся матчей.

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

но не будет работать для:

In [1]: aString="ababa"

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

12
Зачем составлять список из итератора, он просто замедляет процесс.
pradyunsg

2
aString VS astring;)
NexD.

18

Давай, давайте вернемся вместе.

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]

Нет необходимости в регулярных выражениях таким образом.


Я только начал задаваться вопросом: "Есть ли причудливый способ найти подстроку внутри строки в Python?", А затем через 5 минут поиска в Google я нашел ваш код. Спасибо, что поделился!!!
Гепарада

3
Этот код имеет несколько проблем. Так как он работает с открытыми данными, рано или поздно вы столкнетесь, RecursionErrorесли их будет достаточно много. Другой - два одноразовых списка, которые он создает на каждой итерации только для добавления одного элемента, что очень неоптимально для функции поиска строк, которую можно вызывать много раз. Хотя иногда рекурсивные функции кажутся изящными и ясными, к ним следует относиться с осторожностью.
Иван Николаев

11

Если вы просто ищете один символ, это будет работать:

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

Также,

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

Я догадываюсь, что ни один из них (особенно № 2) не очень эффективен.


gr8 решение .. я впечатлен использованием .. split ()
Шантану Патхак

9

это старая тема, но я заинтересовался и хотел поделиться своим решением.

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

Должен возвращать список позиций, где найдена подстрока. Пожалуйста, прокомментируйте, если вы видите ошибку или место для улучшения.


6

Это делает трюк для меня, используя 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

Эта тема немного старая, но у меня это сработало:

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

Ты можешь попробовать :

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

Независимо от того, что решения, предоставленные другими, полностью основаны на доступном методе find () или любых доступных методах.

Каков основной базовый алгоритм для поиска всех вхождений подстроки в строку?

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

Вы также можете наследовать класс str новому классу и использовать эту функцию ниже.

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

Вызов метода

newstr.find_all («Считаете ли вы этот ответ полезным? Тогда проголосуйте за это!», «Это»)


2

Эта функция не просматривает все позиции внутри строки, она не тратит вычислительные ресурсы. Моя попытка:

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

чтобы использовать это, назовите это так:

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

1

При поиске большого количества ключевых слов в документе используйте 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 работает быстрее, чем регулярные выражения в большом списке поисковых слов.


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
Хотя этот код может решить проблему OP, лучше всего включить объяснение того, как ваш код решает проблему OP. Таким образом, будущие посетители могут учиться на вашем посте и применять его к своему коду. SO не сервис кодирования, а ресурс для знаний. Кроме того, высокое качество, полные ответы, скорее всего, будут проголосованы. Эти функции, наряду с требованием, чтобы все сообщения были автономными, являются одними из сильных сторон SO как платформы, которая отличает ее от форумов. Вы можете редактировать, чтобы добавить дополнительную информацию и / или дополнить ваши объяснения исходной документацией
SherylHohman

0

Это решение аналогичного вопроса от хакерранка. Я надеюсь, что это может помочь вам.

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

Вывод:

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

-1

Разрезая, мы находим все возможные комбинации и добавляем их в список и определяем, сколько раз это происходит, используя countфункцию

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

Когда s="test test test test"и f="test"ваш код печатается 4, но ожидается, что OP[0,5,10,15]
Барбсан


-2

пожалуйста, посмотрите на код ниже

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

Питонический путь будет:

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) Как это помогает в вопросе, на который был дан ответ 7 лет назад? 2) Использование lambdaэтого способа не является Pythonic и идет против PEP8 . 3) Это не дает правильного вывода для ситуации ОП
Wondercricket

Pythonic не означает «Используйте столько возможностей Python, сколько вы можете себе представить»
klutt

-2

Вы можете легко использовать:

string.count('test')!

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

Ура!


это должен быть ответ
Максвелл Чендлер

8
Метод count () возвращает число вхождений подстроки в данную строку. Не их местоположение.
Астрид

5
это не удовлетворяет всем случаям, s = 'банан', sub = 'ана'. Sub встречается в этой ситуации дважды, но выполнение s.sub ('ana') вернет 1
Joey daniel darko
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.