获取替换子串的所有可能组合

Get all possible combinations of replacing a substring

Python 给定子字符串和任意替换的文本组合

我有一个字符串:

"foo bar foo foo"

并在该字符串中给定一个子字符串,例如:"foo" 我想得到每个组合,用 "foo" 替换某个任意字符串(它可能有不同的长度)。

例如:

>>> combinations("foo bar foo foo", "foo", "fooer")
{
    "foo bar foo foo",
    "fooer bar foo foo",
    "foo bar fooer foo",
    "foo bar foo fooer",
    "fooer bar fooer foo",
    "fooer bar foo fooer",
    "fooer bar fooer fooer",
    "foo bar fooer fooer",
}

我已经搜索过了,但找不到任何可以帮助我的东西。

我知道我必须使用 itertools.product 进行组合,但是当同一字符串中出现不止一次并且子字符串及其替换长度不同时,我会卡住。

当我得到必须开始替换的索引时:

def indices_substring(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

您可以遵循以下食谱:

  1. 将字符串分隔为单词列表。
  2. 查找要替换的单词的索引。
  3. 创建这些索引的 power-set。
  4. 遍历 power-set 并替换每组索引中的单词。

1。将字符串分隔为单词列表

对任何 Python 用户来说都足够简单:

words = "foo bar foo foo".split()

如果字符串不一定是space-separated,你可以使用正则表达式:

import re

words = re.split("(foo)", "foobarfoofoo")

2。查找要替换的单词的索引

这可以用一个非常简单的方法来完成 list-comprehension:

indices = [i for i, v in enumerate(words) if v == "foo"]

3。创建这些索引的 power-set

官方itertools Recipes page有一个power-set:

from itertools import chain, combinations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

所以有了这个函数,这一步就超级简单了:

power_set = powerset(indices)

4。迭代 power-set 并替换每个集合索引中的单词

为此,我们将首先创建要处理的 words 列表的副本,然后简单地迭代幂集中每个项目的索引并替换这些索引中的单词。为了结束事情,我们将 join 列表:

for replacements in powerset(indices):
    new_words = list(words)
    for index in replacements:
        new_words[index] = "fooer"
    print(" ".join(new_words))

* 如果使用regex版本,应该是''.join(...)

完整代码

所有这些看起来像:

from itertools import chain, combinations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

s = "foo bar foo foo"
to_find = "foo"
to_replace = "fooer"

words = s.split()
# regex: words = re.split(f"({to_find})", s)
indices = [i for i, v in enumerate(words) if v == to_find]
for replacements in powerset(indices):
    new_words = list(words)
    for index in replacements:
        new_words[index] = to_replace
    print(" ".join(new_words))
    # regex: print(''.join(new_words))

给出:

foo bar foo foo
fooer bar foo foo
foo bar fooer foo
foo bar foo fooer
fooer bar fooer foo
fooer bar foo fooer
foo bar fooer fooer
fooer bar fooer fooer