Bir alt dizenin tüm oluşumları nasıl bulunur?


366

Python vardır string.find()ve string.rfind()bir dizede bir alt dizenin dizinini alır.

string.find_all()Bulunan tüm dizinleri (sadece başından itibaren ilk veya sondan itibaren ilk değil) döndürmek gibi bir şey olup olmadığını merak ediyorum .

Örneğin:

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
ne 'ttt'.find_all('tt')dönmeli?
Santiago Alessandri

2
'0' döndürmelidir. Tabii ki, mükemmel dünyada 'ttt'.rfind_all('tt')'1' dönmesi gereken de olmalı
nukl

Yanıtlar:


523

Aradığınızı yapan basit bir yerleşik dize işlevi yoktur, ancak daha güçlü düzenli ifadeleri kullanabilirsiniz :

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

Çakışan eşleşmeler bulmak istiyorsanız, lookahead bunu yapacaktır:

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

Çakışma olmadan ters bulmayı istiyorsanız, olumlu ve olumsuz ileriye bakışı şöyle bir ifadede birleştirebilirsiniz:

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

re.finditerbir jeneratör döndürür , böylece liste yerine bir jeneratör elde []etmek ()için yukarıdaki sonuçları değiştirebilirsiniz;


merhaba, bu ilgilendiren [m.start() for m in re.finditer('test', 'test test test test')]biz nasıl bakabilir testveya text? Çok daha karmaşık hale geliyor mu?
Mart'ta Xpanta

7
Genel olarak düzenli ifadeye bakmak istiyorsunuz: docs.python.org/2/howto/regex.html . Sorunuzun çözümü şu şekildedir: [m.start () için m. Re.finditer ('te [sx] t', 'metin testi metin testi')]
Yotam Vaknin

1
Bu yöntemi kullanmanın zaman karmaşıklığı ne olacak?
Pranjal Mittal

1
@PranjalMittal. Üst veya alt sınır? En iyi, en kötü veya ortalama vaka?
Çılgın Fizikçi

@marcog alt dize parantez veya diğer özel karakterler içeriyorsa ne olur?
Bananach

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

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

Böylece, kendimiz inşa edebiliriz:

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]

Geçici dizeler veya regex'ler gerekmez.


22
Eşleşmeleri örtüşen elde etmek için, yerine yeterli olacaktır start += len(sub)ile start += 1.
Karl Knechtel

4
Önceki yorumunuzun cevabınızda bir postscript olması gerektiğine inanıyorum.
tzot

1
Kodunuz "GATATATGCATATACTT" içindeki substr: "ATAT" bulmak için çalışmaz
Ashish Negi

2
Ayrıca yaptığım yoruma bakın. Bu örtüşen bir eşleşme örneğidir.
Karl Knechtel

4
Davranışını eşleştirmek için re.findall, len(sub) or 1bunun yerine eklemeyi tavsiye ederim len(sub), aksi takdirde bu jeneratör boş alt dize üzerinde asla sonlanmaz.
WGH

46

Tüm maçları (hatta çakışan) almanın (çok verimsiz) bir yolu :

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

25

Yine, eski iplik, ama burada bir jeneratör ve düz kullanarak benim çözüm 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)

Misal

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

İadeler

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

3
bu güzel görünüyor!
fabio.sang

21

re.finditer()Çakışmayan eşleşmeler için kullanabilirsiniz .

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

ancak bunun için çalışmaz:

In [1]: aString="ababa"

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

12
Neden bir yineleyiciden bir liste yapmak, sadece işlemi yavaşlatır.
pradyunsg

2
aString VS büzülme;)
NexD.

18

Gelin, birlikte okuyalım.

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]

Bu şekilde düzenli ifadelere gerek yok.


Ben sadece merak "python bir dize içinde bir alt dize bulmak için süslü bir yolu var" başladı ve sonra 5 dakika googling sonra kodunuzu buldum. Paylaşım için teşekkürler!!!
Geparada

3
Bu kodun birkaç sorunu var. Er ya da geç açık uçlu veriler üzerinde çalıştığı için, RecursionErroryeterli sayıda olay olup olmadığını göreceksiniz . Diğeri, her yinelemede, bir öğeyi eklemek için oluşturduğu iki atma listesidir; bu, muhtemelen birçok kez çağrılabilecek bir dize bulma işlevi için çok yetersizdir. Bazen yinelemeli işlevler zarif ve açık görünse de, dikkatle alınmalıdır.
Ivan Nikolaev

11

Sadece tek bir karakter arıyorsanız, bu işe yarar:

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

Ayrıca,

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

Benim önsezim, bunların hiçbirinin (özellikle # 2) korkunç performans göstermemesi.


gr8 çözümü .. kullanımından çok etkilendim .. split ()
shantanu pathak

9

Bu eski bir iş parçacığı ama ilgimi çekti ve benim çözüm paylaşmak istedim.

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

Alt dizenin bulunduğu konumların bir listesini döndürmelidir. Bir hata veya iyileştirme alanı görüyorsanız lütfen yorum yapın.


6

Bu re.finditer kullanarak benim için hile yapar

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

Bu konu biraz eski ama bu benim için çalıştı:

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

Deneyebilirsin :

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

Başkaları tarafından sağlanan çözümler ne olursa olsun, tamamen mevcut yöntem find () veya mevcut herhangi bir yönteme dayanmaktadır.

Bir dizgideki bir alt dizenin tüm oluşumlarını bulmak için temel temel algoritma nedir?

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

Ayrıca str sınıfını yeni sınıfa devralabilir ve aşağıdaki bu işlevi kullanabilirsiniz.

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

Yöntemin çağrılması

newstr.find_all ('Bu yanıtı faydalı buluyor musun? bunu oyla!', 'this')


2

Bu işlev dize içindeki tüm konumlara bakmaz, bilgi işlem kaynaklarını boşa harcamaz. Benim denemem:

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

kullanmak için şöyle deyin:

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

1

Belgede büyük miktarda anahtar kelime ararken , flashtext kullanın

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, geniş arama sözcükleri listesinde normal ifadeden daha hızlı çalışır.


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
Bu kod OP sorununu çözse de, kodunuzun OP sorununu nasıl ele aldığına dair bir açıklama eklemek en iyisidir. Bu şekilde, gelecekteki ziyaretçiler yayınınızdan bilgi edinebilir ve kendi kodlarına uygulayabilir. SO bir kodlama servisi değil, bilgi kaynağıdır. Ayrıca, yüksek kaliteli ve eksiksiz cevapların kaldırılması daha olasıdır. Bu özellikler, tüm yayınların bağımsız olması şartıyla birlikte, bir platform olarak SO'yu forumlardan ayıran güçlü yönlerinden bazılarıdır. Ek bilgi eklemek ve / veya açıklamalarınızı kaynak belgelerle desteklemek için
düzenleyebilirsiniz

0

Bu, hackerrank'ten benzer bir sorunun çözümü. Umarım bu size yardımcı olabilir.

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

Çıktı:

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

-1

Dilimleyerek mümkün olan tüm kombinasyonları buluruz ve bir listeye ekleriz ve countişlevini kullanarak kaç kez gerçekleştiğini buluruz

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

Kodunuz ne zaman s="test test test test"ve f="test"yazdırılıyor 4, ancak OP bekleniyor[0,5,10,15]
barbsan

Tek bir kelime için yazdığınız kodu güncelleyecek
BONTHA SREEVIDHYA

-2

lütfen aşağıdaki koda bakın

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

Pitonik yol şöyle olurdu:

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) Bu, 7 yıl önce cevaplanan bir soruya nasıl yardımcı olur? 2) Bu şekilde kullanmak lambdaPythonic değildir ve PEP8'e aykırıdır . 3) Bu OP durumu için doğru çıktıyı sağlamaz
Wondercricket

Pythonic, "aklınıza geldiği kadar python özelliklerini kullanın" anlamına gelmez
klutt

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.