จะค้นหาสตริงย่อยทั้งหมดได้อย่างไร?


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')กลับมา?
Santiago Alessandri

2
มันควรกลับเป็น '0' แน่นอนว่าในโลกที่สมบูรณ์แบบนั้นก็ต้องมี'ttt'.rfind_all('tt')เช่นกันซึ่งควรจะกลับมาเป็น '1'
nukl

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]

หากคุณต้องการค้นหาแบบย้อนกลับทั้งหมดโดยไม่มีการเหลื่อมกันคุณสามารถรวม lookahead บวกและลบเป็นนิพจน์เช่นนี้:

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', 'ทดสอบข้อความทดสอบข้อความ')]
Yotam Vaknin

1
อะไรคือความซับซ้อนของเวลาในการใช้วิธีนี้?
Pranjal Mittal

1
@PranjalMittal ขอบบนหรือล่าง? กรณีที่ดีที่สุดเลวร้ายที่สุดหรือโดยเฉลี่ย
นักฟิสิกส์บ้า

@marcog จะเกิดอะไรขึ้นถ้าสตริงย่อยมีวงเล็บหรืออักขระพิเศษอื่น ๆ
Bananach

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]

ไม่จำเป็นต้องใช้สตริงหรือ regexes ชั่วคราว


22
ที่จะได้รับการแข่งขันที่ทับซ้อนกันก็ควรจะพอเพียงที่จะแทนที่ด้วยstart += len(sub) start += 1
Karl Knechtel

4
ฉันเชื่อว่าความคิดเห็นก่อนหน้าของคุณควรเป็นคำลงท้ายในคำตอบของคุณ
tzot

1
รหัสของคุณใช้ไม่ได้กับการค้นหาซับสเตต: "ATAT" ใน "GATATATGCATATACTT"
Ashish Negi

2
ดูความคิดเห็นที่ฉันทำเพิ่มเติม นั่นคือตัวอย่างของการจับคู่ที่ซ้อนทับกัน
Karl Knechtel

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
ทำรายการออกจาก iterator ทำไมมันช้ากระบวนการ
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]

ไม่จำเป็นต้องใช้นิพจน์ทั่วไปด้วยวิธีนี้


ฉันเพิ่งเริ่มสงสัย "มีวิธีแฟนซีเพื่อค้นหา substring ภายในสตริงในหลาม" ... แล้วหลังจาก googling 5 นาทีฉันพบรหัสของคุณ ขอบคุณสำหรับการแบ่งปัน!!!
Geparada

3
รหัสนี้มีปัญหาหลายประการ เนื่องจากมันทำงานกับข้อมูลปลายเปิดไม่ช้าก็เร็วคุณจะชนกับRecursionErrorสิ่งที่เกิดขึ้นมากมาย อีกรายการหนึ่งเป็นรายการที่ทิ้งไปสองรายการที่สร้างขึ้นในการทำซ้ำแต่ละครั้งเพื่อเสริมองค์ประกอบหนึ่งซึ่งมีประโยชน์อย่างมากสำหรับฟังก์ชั่นการค้นหาสตริงซึ่งอาจเรียกได้หลายครั้ง แม้ว่าบางครั้งฟังก์ชั่นวนซ้ำจะดูสวยงามและชัดเจน แต่ก็ควรใช้ด้วยความระมัดระวัง
Ivan Nikolaev

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 ()
shantanu pathak

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 ทำงานเร็วกว่า regex ในรายการคำค้นหาขนาดใหญ่


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 ในฐานะแพลตฟอร์มซึ่งแตกต่างจากฟอรัม คุณสามารถแก้ไขเพื่อเพิ่มข้อมูลเพิ่มเติม & / หรือเสริมคำอธิบายของคุณด้วยเอกสารต้นฉบับ
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]
barbsan

ได้เขียนคำเดียวจะอัปเดตรหัส
BONTHA SREEVIDHYA

-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

วิธี pythonic จะเป็น:

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) สิ่งนี้ไม่ได้ให้ผลลัพธ์ที่ถูกต้องสำหรับสถานการณ์
OPs

Pythonic ไม่ได้หมายความว่า "ใช้คุณสมบัติของงูใหญ่เท่าที่คุณจะนึกได้"
klutt

-2

คุณสามารถใช้:

string.count('test')!

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

ไชโย!


นี่ควรเป็นคำตอบ
Maxwell Chandler

8
จำนวนสตริง () วิธีการส่งกลับจำนวนที่เกิดขึ้นของสตริงย่อยในสตริงที่กำหนด ไม่ใช่ตำแหน่งของพวกเขา
Astrid

5
สิ่งนี้ไม่เป็นที่พอใจทุกกรณี s = 'Banana', sub = 'ana' Sub เกิดขึ้นในสถานการณ์นี้สองครั้ง แต่การทำ s.sub ('ana') จะกลับมา 1
Joey daniel darko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.