Як знайти всі входження в підрядку?


365

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'
11:47

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 in re.finditer ('te [sx] t', 'test test text test')]
Йотам Вакнін

1
Якою буде часова складність використання цього методу?
Пранджал Міттал

1
@PranjalMittal. Верхня або нижня межа? Найкращий, гірший чи середній випадок?
Божевільний фізик

@marcog, що, якщо підрядок містить круглі дужки або інші спеціальні символи?
Бананах

109
>>> 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"
Ashish Negi

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 хв гуглінгу я знайшов ваш код. Дякую, що поділились!!!
Гепарада

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
Хоча цей код може вирішити проблему ОП, краще включити пояснення щодо того, як ваш код вирішує проблему ОП. Таким чином майбутні відвідувачі можуть дізнатисясь із вашої публікації та застосувати її до власного коду. ТАК - це не програма кодування, а ресурс для знань. Крім того, більш якісні, повні відповіді, швидше за все, будуть сприйняті. Ці функції, поряд з вимогою, що всі повідомлення є автономними, є деякою сильною стороною 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, але ОП очікується[0,5,10,15]
barbsan


-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цього способу не є пітонічним і йде проти PEP8 . 3) Це не дає правильного результату для ситуації з ОП
Wondercricket

Pythonic не означає "Використовуйте стільки можливостей python, скільки ви можете подумати"
klutt

-2

Ви можете легко використовувати:

string.count('test')!

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

Ура!


це повинна бути відповідь
Максвелл Чандлер

8
Метод count () повертає кількість входів підрядків у заданій рядку. Не їх місцезнаходження.
Астрід

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