2017-10-12 55 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()。但是,我不認爲這是可能的。建議使用正則表達式的另一個答案始終是一個好的呼叫。