如何找到子字符串的所有出现?

586 投票
33 回答
810331 浏览
提问于 2025-04-16 09:48

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]

如果你想要统计出现的次数,可以查看这个链接:统计一个子字符串在字符串中出现的次数

33 个回答

87

这里有一种(效率很低)的方法,可以获取所有(也就是说,包括重叠的)匹配项:

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

这个方法也适用于多个单词组成的子词。

s = "Find THIS SUB-WORD in this sentence with THIS SUB-WORD"
sub = "THIS SUB-WORD"
[i for i in range(len(s)) if s.startswith(sub, I)]
# [5, 41]
178
>>> 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]

不需要临时字符串或者正则表达式。

786

没有简单的内置字符串函数可以满足你的需求,但你可以使用更强大的正则表达式来实现:

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

如果你想找到重叠的匹配项,可以使用前瞻来做到这一点:

[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会返回一个生成器,所以你可以把上面的[]改成(),这样就能得到一个生成器,而不是一个列表。如果你只打算遍历结果一次,这样会更高效。

撰写回答