Làm thế nào để tìm tất cả các lần xuất hiện của một chuỗi con?


365

Python có string.find()string.rfind()để lấy chỉ mục của một chuỗi con trong một chuỗi.

Tôi đang tự hỏi liệu có một cái gì đó giống như string.find_all()có thể trả về tất cả các chỉ mục được tìm thấy (không chỉ đầu tiên từ đầu hoặc đầu tiên từ cuối).

Ví dụ:

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
Nên 'ttt'.find_all('tt')trả lại cái gì?
Santiago Alessandri

2
nó sẽ trả về '0'. Tất nhiên, trong thế giới hoàn hảo cũng phải có 'ttt'.rfind_all('tt'), điều đó sẽ trả về '1'
nukl

2
Có vẻ như là một bản sao của stackoverflow.com/questions/3873361/ từ
nu everest

Câu trả lời:


523

Không có hàm chuỗi dựng sẵn đơn giản nào thực hiện những gì bạn đang tìm kiếm, nhưng bạn có thể sử dụng các biểu thức chính quy mạnh hơn :

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

Nếu bạn muốn tìm các kết quả trùng lặp, lookahead sẽ làm điều đó:

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

Nếu bạn muốn tìm ngược lại mà không có sự trùng lặp, bạn có thể kết hợp giao diện tích cực và tiêu cực thành một biểu thức như thế này:

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

re.finditertrả về một trình tạo , do đó bạn có thể thay đổi []ở trên để ()lấy một trình tạo thay vì một danh sách sẽ hiệu quả hơn nếu bạn chỉ lặp lại các kết quả một lần.


xin chào, liên quan đến điều này [m.start() for m in re.finditer('test', 'test test test test')], làm thế nào chúng ta có thể tìm kiếm testhoặc text? Nó trở nên phức tạp hơn nhiều?
xpanta

7
Bạn muốn xem xét biểu thức chính quy nói chung: docs.python.org/2/howto/regex.html . Giải pháp cho câu hỏi của bạn sẽ là: [m.start () cho m trong re.finditer ('te [sx] t', 'kiểm tra văn bản kiểm tra văn bản')]
Yotam Vaknin

1
Điều gì sẽ là phức tạp thời gian của việc sử dụng phương pháp này?
Pranjal Găngal

1
@PranjalMittal. Thượng hay hạ giới hạn? Trường hợp tốt nhất, tồi tệ nhất hoặc trung bình?
Nhà vật lý điên

@marcog nếu chuỗi con chứa dấu ngoặc đơn hoặc các ký tự đặc biệt khác thì sao?
Tráng

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

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

Vì vậy, chúng ta có thể tự xây dựng nó:

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]

Không có chuỗi tạm thời hoặc regexes yêu cầu.


22
Để có được các trận đấu chồng chéo, nó cần đủ để thay thế start += len(sub)bằng start += 1.
Karl Knechtel

4
Tôi tin rằng bình luận trước đây của bạn nên là một phần tái bút trong câu trả lời của bạn.
tzot

1
Mã của bạn không hoạt động để tìm chất nền: "ATAT" trong "GATATATGCATATACTT"
Ashish Negi

2
Xem các bình luận tôi đã thực hiện ngoài. Đó là một ví dụ về một trận đấu chồng chéo.
Karl Knechtel

4
Để phù hợp với hành vi của re.findall, tôi khuyên bạn nên thêm len(sub) or 1thay vì len(sub), nếu không, trình tạo này sẽ không bao giờ chấm dứt trên chuỗi con trống.
WGH

45

Đây là một cách (rất không hiệu quả) để có được tất cả các kết quả khớp (thậm chí là chồng chéo):

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

25

Một lần nữa, chủ đề cũ, nhưng đây là giải pháp của tôi bằng cách sử dụng một trình tạo và đơn giản 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)

Thí dụ

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

trả lại

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

3
cái này trông thật đẹp
fabio.sang 28/03/19

21

Bạn có thể sử dụng re.finditer()cho các trận đấu không chồng chéo.

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

nhưng sẽ không làm việc cho:

In [1]: aString="ababa"

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

12
Tại sao tạo một danh sách từ một iterator, nó chỉ làm chậm quá trình.
pradyunsg

2
aString VS làm se;)
NexD.

18

Hãy đến, chúng ta hãy cùng nhau tái diễn.

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]

Không cần biểu thức chính quy theo cách này.


Tôi mới bắt đầu tự hỏi "có một cách thú vị để xác định một chuỗi con bên trong một chuỗi trong python" ... và sau 5 phút tôi đã tìm thấy mã của bạn. Cám ơn vì đã chia sẻ!!!
Geparada

3
Mã này có một số vấn đề. Vì nó hoạt động trên dữ liệu kết thúc sớm hay muộn, bạn sẽ gặp phải RecursionErrornếu có nhiều sự cố xảy ra. Một danh sách khác là hai danh sách bỏ đi mà nó tạo ra trên mỗi lần lặp chỉ với mục đích nối thêm một phần tử, rất tối ưu cho hàm tìm chuỗi, có thể được gọi là rất nhiều lần. Mặc dù đôi khi các chức năng đệ quy có vẻ thanh lịch và rõ ràng, chúng nên được thực hiện một cách thận trọng.
Ivan Nikolaev

11

Nếu bạn chỉ tìm kiếm một nhân vật, điều này sẽ hoạt động:

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

Cũng thế,

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

Linh cảm của tôi là không ai trong số này (đặc biệt là # 2) là người thực hiện khủng khiếp.


giải pháp gr8 .. tôi rất ấn tượng với việc sử dụng .. split ()
shantanu pathak

9

đây là một chủ đề cũ nhưng tôi đã quan tâm và muốn chia sẻ giải pháp của tôi.

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

Nó sẽ trả về một danh sách các vị trí nơi chuỗi con được tìm thấy. Hãy bình luận nếu bạn thấy một lỗi hoặc phòng cho ngẫu hứng.


6

Đây là mẹo cho tôi khi sử dụng 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

Chủ đề này hơi cũ nhưng điều này làm việc cho tôi:

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

Bạn co thể thử :

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

Bất cứ giải pháp nào được cung cấp bởi người khác đều hoàn toàn dựa trên phương thức có sẵn find () hoặc bất kỳ phương thức có sẵn nào.

Thuật toán cơ bản cốt lõi để tìm tất cả các lần xuất hiện của một chuỗi con trong chuỗi là gì?

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

Bạn cũng có thể kế thừa lớp str sang lớp mới và có thể sử dụng chức năng này bên dưới.

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

Gọi phương thức

newstr.find_all ('Bạn có thấy câu trả lời này hữu ích không? sau đó upvote này!', 'this')


2

Hàm này không xem xét tất cả các vị trí bên trong chuỗi, nó không lãng phí tài nguyên tính toán. Tôi thử

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

để sử dụng nó gọi nó như thế này:

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

1

Khi tìm kiếm một lượng lớn từ khóa trong tài liệu, hãy sử dụng 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 chạy nhanh hơn regex trong danh sách lớn các từ tìm kiếm.


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
Mặc dù mã này có thể giải quyết vấn đề của OP, tốt nhất là bao gồm một lời giải thích về cách mã của bạn giải quyết vấn đề của OP. Theo cách này, khách truy cập trong tương lai có thể học hỏi từ bài đăng của bạn và áp dụng nó vào mã của riêng họ. SO không phải là một dịch vụ mã hóa, mà là một nguồn tài nguyên cho kiến ​​thức. Ngoài ra, chất lượng cao, câu trả lời đầy đủ có nhiều khả năng được nâng cao. Các tính năng này, cùng với yêu cầu tất cả các bài đăng đều khép kín, là một số điểm mạnh của SO như một nền tảng, phân biệt nó với các diễn đàn. Bạn có thể chỉnh sửa để thêm thông tin bổ sung & / hoặc để bổ sung giải thích của bạn với tài liệu nguồn
SherylHohman

0

Đây là giải pháp cho một câu hỏi tương tự từ hackerrank. Tôi hy vọng điều này có thể giúp bạn.

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

Đầu ra:

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

-1

Bằng cách cắt, chúng tôi tìm thấy tất cả các kết hợp có thể và nối chúng vào danh sách và tìm số lần xảy ra bằng cách sử dụng counthàm

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

Khi nào s="test test test test"f="test"mã của bạn được in 4, nhưng OP dự kiến[0,5,10,15]
barbsan

Đã viết cho một từ duy nhất sẽ cập nhật mã
BONTHA SREEVIDHYA

-2

xin vui lòng xem mã dưới đây

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

Cách thức pythonic sẽ là:

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) Làm thế nào điều này giúp một câu hỏi đã được trả lời 7 năm trước? 2) Sử dụng lambdacách này không phải là Pythonic và đi ngược lại PEP8 . 3) Điều này không cung cấp đầu ra chính xác cho tình huống OP
Wondercricket

Pythonic không có nghĩa là "Sử dụng nhiều tính năng của trăn như bạn có thể nghĩ"
klutt

-2

Bạn có thể dễ dàng sử dụng:

string.count('test')!

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

Chúc mừng!


đây sẽ là câu trả lời
Maxwell Chandler

8
Phương thức đếm chuỗi () trả về số lần xuất hiện của một chuỗi con trong chuỗi đã cho. Không phải vị trí của họ.
Astrid

5
điều này không thỏa mãn tất cả các trường hợp, s = 'chuối', sub = 'ana'. Sub xảy ra trong tình huống này hai lần nhưng làm s.sub ('ana') sẽ trở lại 1
Joey daniel darko
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.