Python - 替换每第n次出现的字符串

时间:2017-10-12 09:03:59

标签: python

我从问题Replace nth occurrence of substring in string中提取了以下代码段。

将替换第n个子字符串的单个匹配项。但是我想替换每个第n个子串

的所有出现次数

所以如果字符串中出现30个子字符串,我会想要替换entires 10和20,例如,但我不知道如何实现这一点

def nth_repl(s, sub, repl, nth):
    find = s.find(sub)
    # if find is not p1 we have found at least one match for the substring
    i = find != -1
    # loop util we find the nth or we find no match
    while find != -1 and i != nth:
        # find + 1 means we start at the last match start index + 1
        find = s.find(sub, find + 1)
        i += 1
    # if i  is equal to nth we found nth matches so replace
    if i == nth:
        return s[:find]+repl+s[find + len(sub):]
    return s

5 个答案:

答案 0 :(得分:2)

我会使用re.sub替换函数来跟踪对象,以避免使用全局变量。

s = "hello world "*30

import re

class RepObj:
    def __init__(self,replace_by,every):
        self.__counter = 0
        self.__every = every
        self.__replace_by = replace_by

    def doit(self,m):
        rval = m.group(1) if self.__counter % self.__every else self.__replace_by
        self.__counter += 1
        return rval

r = RepObj("earth",5)  # init replacement object with replacement and freq
result = re.sub("(world)",r.doit,s)

print(result)

结果:

hello earth hello world hello world hello world hello world hello earth hello world hello world hello world hello world hello earth hello world hello world hello world hello world hello earth hello world hello world hello world hello world hello earth hello world hello world hello world hello world hello earth hello world hello world hello world hello world 
编辑:不需要帮助对象,礼貌地使用Jon Clements(一如既往的智能解决方案),使用lambdacounter创建一个单行:

import re,itertools

s = "hello world "*30

result = re.sub('(world)', lambda m, c=itertools.count(): m.group() if next(c) % 5 else 'earth', s)

您可以调整计数器以满足您的特定需求,并使其非常复杂,因为逻辑允许。

答案 1 :(得分:2)

你从上一个问题得到的代码是一个很好的起点,只需要一个最小的适应性就可以让它在每第n次发生时改变:

def nth_repl_all(s, sub, repl, nth):
    find = s.find(sub)
    # loop util we find no match
    i = 1
    while find != -1:
        # if i  is equal to nth we found nth matches so replace
        if i == nth:
            s = s[:find]+repl+s[find + len(sub):]
            i = 0
        # find + len(sub) + 1 means we start after the last match
        find = s.find(sub, find + len(sub) + 1)
        i += 1
    return s

答案 2 :(得分:0)

我不太清楚你明白这里有什么意思。
假设您想要在a字符串中将A的每{2}替换为abababab,以便最后abAbabAb

您可以重复使用上面修改的代码段并使用递归方法。

这里的想法是找到并替换子串的第n次出现并返回s[:find] + nth_repl(s[find:], sub, repl, nth)的连接

def nth_repl(s, sub, repl, nth):

    find = s.find(sub)

    # if find is not p1 we have found at least one match for the substring
    i = 1

    # loop util we find the nth or we find no match
    while find != -1 and i != nth:
        # find + 1 means we start at the last match start index + 1
        find = s.find(sub, find + 1)
        i += 1
    # if i  is equal to nth we found nth matches so replace

    if i == nth:
        s= s[:find]+repl+s[find+1:]
        return s[:find] + nth_repl(s[find:], sub, repl, nth)
    else:
        return s

答案 3 :(得分:0)

原始Python,没有重新

a = 'hello world ' * 30
b = ['zzz' + x if (idx%3 == 0) and idx > 0 else x for idx,x in enumerate(a.split('world'))]

print 'world'.join(b).replace('worldzzz', 'earth')

Out[25]: 'hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth hello world hello world hello earth '

答案 4 :(得分:0)

我们不能双重使用string.replace方法吗?

例如:

a = "foobarfoofoobarbar"
print(a)

>> foobarfoofoobarbar

n_instance_to_replace = 2
a = a.replace("foo", "FOO", n_instance_to_replace).replace("FOO","foo", n_instance_to_replace - 1)
print(a)

>> foobarFOOfoobarbar

基本上,第一个.replace("foo", "FOO", n_instance_to_replace)"foo" 的所有子字符串转换为第二次出现"FOO",然后第二个.replace("FOO", "foo", n_instance_to_replace)全部转为全部我们想要更改回"FOO"之前的"foo" s。

这个可以扩展来改变每个第n个重复子字符串,如下所示:

a = "foobarfoofoobarbar"*3 # create string with repeat "foo"s
n_instance = 2  # set nth substrings of "foo" to be replaced
# Replace nth subs in supstring
for n in range(n_instance, a.count("foo")+n_instance, n_instance)[::-1]:
    a = a.replace("foo","FOO", n).replace("FOO","foo", n-1)
    print(n, n-1, a)

>> 10 9 foobarfoofoobarbarfoobarfoofoobarbarfoobarfoofoobarbar
>> 8 7 foobarfoofoobarbarfoobarfoofoobarbarfoobarFOOfoobarbar
>> 6 5 foobarfoofoobarbarfoobarfooFOObarbarfoobarFOOfoobarbar
...
>> 2 1 foobarFOOfoobarbarFOObarfooFOObarbarfoobarFOOfoobarbar

range()基本上设置为从"foo"字符串的 end 开始查找每个a的索引。作为一个功能,这可能只是:

def repl_subst(sup="foobarfoofoobarbar"*5, sub="foo", sub_repl="FOO",  n_instance=2):
    for n in range(n_instance, sup.count(sub)+n_instance, n_instance)[::-1]:
        sup = sup.replace(sub, sub_repl, n).replace(sub_repl, sub, n-1)
    return sup

a = repl_substr()

很棒的是,无需外部包

编辑:我认为我误解了您的问题,现在看到实际上想要继续替换"foo"的每个第n个实例而不是单个实例。我会考虑是否仍然可以使用.replace()。但是,我认为这是不可能的。另一个建议使用正则表达式的答案总是很好的。