2017-10-12 52 views
2

我已经从问题Replace nth occurrence of substring in string中拉下面的代码片段。Python - 替换每个出现的字符串

它将替换第n个子字符串处的单个事件。然而,我想要替换所有发生在每个第n个子字符串

因此,如果有30个字符串中出现的子字符串,我想要替换条目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 
+0

'every every nth'立刻让人想起模数运算符'%',在这里你有一个增量循环,每一遍都检查'incrementor%n',如果为零则让你改变 – Cwissy

回答

2

你从以前的问题得到的代码是一个很好的起点,只有最小的适应需要有改变它每n个occurence:

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 
+0

好的,这是我的递归思想的迭代解决方案。它看起来整洁 –

+0

这似乎只能取代现在的第一个实例 – AlexW

+0

@AlexW你可以展示一个例子,它只替换第一次出现吗?在我的测试中,它工作正常... –

1

我会用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 

编辑:没有必要的辅助对象,礼貌乔恩·克莱门茨(智能解决方案总是),使用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) 

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

+0

谁需要一个类?尝试:'re.sub('(world)',lambda m,c = itertools.count():m.group()if next(c)%5 else'earth',s)':) –

+0

无论如何.. 。如果你要采用类方法 - 你应该使类的'__call__'方法成为'do_',然后你将'RepObj('earth',5)'直接传递给're.sub'。 –

+0

@JonClements是的,类的方法有点矫枉过正 –

0

我不知道,了解很清楚什么是你的意图在这里。
比方说,你想要的字符串ababababA取代的a每2发生所以到底有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 
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 ' 
+0

这是我的第一次尝试。但尝试替换“你好”。在这种情况下'split'生成一个空字符串。顺便说一句,为什么这个“zzz”?很奇怪 –

+0

是的,我明白了。但是那空串并不重要。 OP不希望第一个被替换。即使他想要,他也可以随时更换第一个。 – jf328

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"

可扩展改变每一个第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"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()。但是,我不认为这是可能的。建议使用正则表达式的另一个答案始终是一个好的呼叫。