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

253

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

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

Например:

string = "test test test test"

print string.find('test') # 0
print string.rfind('test') # 15

#that the goal
print string.find_all('test') # [0,5,10,15]
  • 9
    что должен 'ttt'.find_all('tt') ?
  • 2
    он должен вернуть «0». Конечно, в идеальном мире также должен быть 'ttt'.rfind_all('tt') , который должен возвращать' 1 '
Показать ещё 2 комментария
Теги:
string

16 ответов

375
Лучший ответ

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

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 возвращает генератор, поэтому вы можете изменить [] в приведенном выше re.finditer на () чтобы получить генератор вместо списка, который будет более эффективен, если вы будете только повторять результаты один раз.

  • 0
    Привет, относительно этого [m.start() for m in re.finditer('test', 'test test test test')] , как мы можем искать test или text ? Становится ли это намного сложнее?
  • 7
    Вы хотите посмотреть на регулярные выражения в целом: docs.python.org/2/howto/regex.html . Решением вашего вопроса будет: [m.start () для m в re.finditer ('te [sx] t', 'text test text test')]
Показать ещё 3 комментария
85
>>> 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]

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

  • 18
    Чтобы получить совпадающие совпадения, достаточно заменить start += len(sub) на start += 1 .
  • 4
    Я считаю, что ваш предыдущий комментарий должен быть постскриптумом в вашем ответе.
Показать ещё 4 комментария
36

Здесь (очень неэффективный) способ получить все (т.е. даже совпадение):

>>> string = "test test test test"
>>> [i for i in range(len(string)) if string.startswith('test', i)]
[0, 5, 10, 15]
  • 0
    @BlaXpirit: true, выходные данные range() начинаются с нуля. Спасибо...
17

Вы можете использовать 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)]
  • 11
    Зачем составлять список из итератора, он просто замедляет процесс.
  • 2
    aString VS astring;)
Показать ещё 1 комментарий
15

Опять старый поток, но здесь мое решение использует генератор и обычный 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')]
  • 0
    это выглядит красиво!
15

Приходите, давайте возместим вместе.

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]

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

  • 0
    Я только начал задаваться вопросом: "Есть ли причудливый способ найти подстроку внутри строки в Python?", А затем через 5 минут поиска в Google я нашел ваш код. Спасибо, что поделился!!!
  • 2
    Этот код имеет несколько проблем. Поскольку он работает с открытыми данными рано или поздно, вы столкнетесь с RecursionError если будет достаточно много вхождений. Другой - два одноразовых списка, которые он создает на каждой итерации только для добавления одного элемента, что очень неоптимально для функции поиска строк, которую можно вызывать много раз. Хотя иногда рекурсивные функции кажутся изящными и ясными, к ним следует относиться с осторожностью.
7

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

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

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

7

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

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) не ужасен.

4

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

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

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

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

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

0
5
10
15
1

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

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

  def find_all (строка, подстрока):    ""   Функция: Возврат всего индекса подстроки в строку   Аргументы: Строка и строка поиска   Возврат: возврат списка    ""   length = len (подстрока)   с = 0   indexes = []   в то время как c < Len (строка):       if string [c: c + length] == substring:           indexes.append(с)       с = с + 1   индексы возврата
Код>

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

  class newstr (str):
def find_all (строка, подстрока):    ""   Функция: Возврат всего индекса подстроки в строку   Аргументы: Строка и строка поиска   Возврат: возврат списка    ""   length = len (подстрока)   с = 0   indexes = []   в то время как c < Len (строка):       if string [c: c + length] == substring:           indexes.append(с)       с = с + 1   индексы возврата
Код>

Вызов метода

newstr.find_all ( "Вы находите этот ответ полезным?" это!", 'this')

0

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

string.count('test')!

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

Ура!

  • 0
    это должен быть ответ
0

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню