繁体   English   中英

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

[英]Python - replace every nth occurrence of string

我已经从问题中提取了以下代码片段替换字符串中第 n 次出现的子字符串

这将替换第 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

我将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 

编辑:不需要帮助对象,要感谢乔恩·克莱门茨(一如既往的智能解决方案),使用一个lambda和一个counter来创建单线:

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)

您可以根据自己的特定需求调整计数器,使其变得非常复杂,因为逻辑允许这样做。

您从上一个问题获得的代码是一个不错的起点,并且只需要进行最小的调整就可以使每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

替换每个第 n 个子字符串的最有效方法之一是将字符串按所有子字符串拆分,然后按每个第 n 个连接。

这需要对字符串进行恒定次数的迭代:

def replace_nth(s, sub, repl, n=1):
    chunks = s.split(sub)
    size = len(chunks)
    rows = size // n + (0 if size % n == 0 else 1)
    return repl.join([
        sub.join([chunks[i * n + j] for j in range(n if (i + 1) * n < size else size - i * n)])
        for i in range(rows)
    ])

例子:

replace_nth('1 2 3 4 5 6 7 8 9 10', ' ', ',', 2)
>>> 1 2,3 4,5 6,7 8,9 10

replace_nth('1 2 3 4 5 6 7 8 9 10', ' ', '|', 3)
>>> 1 2 3|4 5 6|7 8 9|10

我不确定是否很清楚您的意图。
假设您要在字符串abababab中用A替换第二次出现的a ,以便最后使用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

原始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 '

我们不能双重使用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() 但是,我认为这不可能。 建议使用正则表达式的另一个答案总是一个不错的选择。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM