在字符串中找到第n个子串出现位置

182 投票
27 回答
314309 浏览
提问于 2025-04-15 16:53

这看起来应该很简单,但我刚学Python,想用最符合Python风格的方法来做。

我想找到一个字符串中某个子字符串出现的第n次的位置。

应该有类似我想做的事情的办法,就是

mystring.find("substring", 2nd)

在Python中怎么实现这个呢?

27 个回答

52

这段代码会找到字符串中第二次出现的子字符串。

def find_2nd(string, substring):
   return string.find(substring, string.find(substring) + 1)

补充:我没有仔细考虑性能问题,不过快速的递归方法可以帮助找到第n次出现的情况:

def find_nth(string, substring, n):
   if (n == 1):
       return string.find(substring)
   else:
       return string.find(substring, find_nth(string, substring, n - 1) + 1)
128

这里有一个更符合Python风格的简单迭代解决方案:

def find_nth(haystack: str, needle: str, n: int) -> int:
    start = haystack.find(needle)
    while start >= 0 and n > 1:
        start = haystack.find(needle, start+len(needle))
        n -= 1
    return start

示例:

>>> find_nth("foofoofoofoo", "foofoo", 2)
6

如果你想找到第n个重叠needle出现,可以每次增加1,而不是增加len(needle),像这样:

def find_nth_overlapping(haystack, needle, n):
    start = haystack.find(needle)
    while start >= 0 and n > 1:
        start = haystack.find(needle, start+1)
        n -= 1
    return start

示例:

>>> find_nth_overlapping("foofoofoofoo", "foofoo", 2)
3

这个方法比Mark的版本更容易理解,而且不需要像分割版本那样额外占用内存,也不需要导入正则表达式模块。它还遵循了Python的一些原则,和各种re方法相比更符合这些原则:

  1. 简单胜于复杂。
  2. 扁平胜于嵌套。
  3. 可读性很重要。
93

我觉得马克的迭代方法是比较常见的做法。

这里有一种用字符串分割的替代方法,这种方法在处理查找相关的过程时通常很有用:

def findnth(haystack, needle, n):
    parts= haystack.split(needle, n+1)
    if len(parts)<=n+1:
        return -1
    return len(haystack)-len(parts[-1])-len(needle)

还有一个快速的方法(虽然有点不太干净,因为你需要选择一些无法匹配目标的杂项):

'foo bar bar bar'.replace('bar', 'XXX', 1).find('bar')

撰写回答