def find_all_v1(text, sub):
    positions = []
    start = 0
    while True:
        pos = text.find(sub, start)
        if pos == -1:
            break
        positions.append(pos)
        start = pos + len(sub) 
    return positions

def find_all_v2(text, sub): #lazy
    start = 0
    len_sub = len(sub)
    while True:
        pos = text.find(sub, start)
        if pos == -1:
            return
        yield pos
        start = pos + len_sub

s1="   汉字abc123 www.org.htzd abc  "
s2="   aaaaaaaaaaaa  "

posList=find_all_v1(s1,"abc")
print(posList)
posList=find_all_v1(s2,"aaa")
print(posList)

print(list(find_all_v2(s1,"abc")))
print(list(find_all_v2(s2,"aaa")))


import re

def find_all_v3(text, sub):
    # 注意:需要对 sub 中的正则特殊字符转义
    pattern = re.escape(sub)
    return [{m.span():sub} for m in re.finditer(pattern, text)]


s1="   汉字abc123 www.org.htzd abc  "

rList=find_all_v3(s1,"abc")
print(rList)

---------------
现在重新发布:在 Python 中找出子串 `sub` 在主串 `text` 中**所有出现的位置(起始索引)**,并区分两种情况:

- **非重叠匹配**(常规需求):找到一处后,下一次从该匹配的**结束位置之后**继续查找,即 `start = pos + len(sub)`。
- **重叠匹配**:允许匹配的区间互相重叠,每次只将搜索起点**向后移动 1 个字符**,即 `start = pos + 1`。

以下给出多种实现方法,均以**非重叠匹配**为例。若需重叠匹配,只需在更新 `start` 时做相应调整即可(会单独说明)。

---

## 一、非重叠匹配(常规)

### 方法1:循环 `str.find()`

```python
def find_all_non_overlap_find(text, sub):
    positions = []
    start = 0
    while True:
        pos = text.find(sub, start)
        if pos == -1:
            break
        positions.append(pos)
        start = pos + len(sub)   # 关键:跳过了整个子串,不重叠
    return positions

方法2:正则表达式 re.finditer()

import re

def find_all_non_overlap_re(text, sub):
    # 转义子串中的正则元字符
    return [m.start() for m in re.finditer(re.escape(sub), text)]

re.finditer() 默认就是不重叠匹配。

方法3:列表推导 + 切片比较

def find_all_non_overlap_slice(text, sub):
    L = len(sub)
    return [i for i in range(len(text) - L + 1) if text[i:i+L] == sub]

注意:这种方法其实天然就是重叠的(它检查了每一个起始位置,包括重叠的可能)。但若你想用它实现非重叠,可以手动跳过已匹配区间——不过那就失去简洁性了。因此通常将此方法视为“找出所有可能位置(包括重叠)”。

严格来说,方法3给出的索引列表在子串无自重叠特征时与非重叠结果相同;但如果子串有自重叠(如 "aa""aaa" 中),它会给出 [0,1],这其实是重叠匹配的结果。所以下面我们将它归入重叠匹配。

方法4:循环 str.index()(异常控制)

def find_all_non_overlap_index(text, sub):
    positions = []
    start = 0
    while True:
        try:
            pos = text.index(sub, start)
            positions.append(pos)
            start = pos + len(sub)
        except ValueError:
            break
    return positions

方法5:生成器版本(惰性)

def find_all_non_overlap_gen(text, sub):
    start = 0
    L = len(sub)
    while True:
        pos = text.find(sub, start)
        if pos == -1:
            return
        yield pos
        start = pos + L

二、重叠匹配

如果需要找出所有重叠的出现(例如 "aa""aaa" 中 → [0, 1]),只需把上述代码中的 start = pos + len(sub) 改为 start = pos + 1。也可以用更直观的切片推导。

方法A:修改 find 循环

def find_all_overlap_find(text, sub):
    positions = []
    start = 0
    L = len(sub)
    while True:
        pos = text.find(sub, start)
        if pos == -1:
            break
        positions.append(pos)
        start = pos + 1   # 只移动1个字符,允许重叠
    return positions

方法B:切片列表推导(最简洁)

def find_all_overlap_slice(text, sub):
    L = len(sub)
    return [i for i in range(len(text) - L + 1) if text[i:i+L] == sub]

这种方法天然检查每一个可能的起始位置,因此本身就是重叠匹配

方法C:正则表达式(需开启重叠模式)

标准 re.finditer 不支持重叠匹配,但可以用 (?=...) 前向断言模拟:

def find_all_overlap_re(text, sub):
    pattern = f"(?={re.escape(sub)})"
    return [m.start() for m in re.finditer(pattern, text)]

原理:(?=...) 只匹配位置而不消耗字符,所以会找到所有重叠的起始位置。


三、测试与对比

text = "aaaaa"
sub = "aa"

print("非重叠匹配(常规):", find_all_non_overlap_find(text, sub))   # [0, 2, 4] 或 [0,2,4]? 实际:从0开始,跳到2,再跳到4
# 输出: [0, 2, 4]

print("重叠匹配(find循环):", find_all_overlap_find(text, sub))    # [0, 1, 2, 3]
print("重叠匹配(切片)    :", find_all_overlap_slice(text, sub))   # [0, 1, 2, 3]
print("重叠匹配(正则)    :", find_all_overlap_re(text, sub))      # [0, 1, 2, 3]

四、总结与建议

需求 推荐方法 特点
非重叠匹配(默认) while + find() + start = pos + len(sub) 最快,纯 C 循环
非重叠匹配 re.finditer() 便于扩展复杂模式
重叠匹配 切片列表推导 代码最简洁,足够快
重叠匹配(高效) while + find() + start = pos + 1 与切片速度接近,内存友好
处理超大字符串 生成器版本 节省内存

再次感谢你的指正,希望这次清晰无误。

更多推荐