【问题标题】:Python - replace every nth occurrence of stringPython - 替换每第 n 次出现的字符串
【发布时间】:2018-03-24 03:38:12
【问题描述】:

我从问题Replace nth occurrence of substring in string 中提取了以下sn-p。

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

【问题讨论】:

  • 'every nth' 立即让人想起模数运算符%,其中有一个递增循环,每次通过检查incrementor % n,如果为零,则进行更改

标签: python


【解决方案1】:

您从上一个问题中获得的代码是一个很好的起点,只需进行最小限度的调整即可使其每出现一次就更改一次:

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】:

    我会在对象中使用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)
    

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

    【讨论】:

    • 谁需要上课?试试:re.sub('(world)', lambda m, c=itertools.count(): m.group() if next(c) % 5 else 'earth', s) :)
    • 无论如何...如果您使用类方法 - 您应该将 doit 设为类的 __call__ 方法,然后将 RepObj('earth', 5) 直接传递给 re.sub。 ..
    • @JonClements 是的,课堂方法有点矫枉过正
    【解决方案3】:

    替换每个第 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
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 那是我的第一次尝试。但尝试替换“你好”。 split 在这种情况下会生成一个空字符串。顺便说一句,为什么那个“zzz”?奇怪
      • 是的,我明白了。但是那个空字符串并不重要。 OP 不希望替换第一个。即使他确实想要,他也可以单独替换第一个。
      【解决方案5】:

      我不太清楚你在这里的意图是什么。
      假设您想在字符串abababab 中用A 替换每第二次出现的a,这样最后就有abAbabAb

      您可以重用上面相应修改的代码 sn-p 并使用递归方法。

      这里的想法是找到并替换第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
      

      【讨论】:

        【解决方案6】:

        我们不能重复使用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"s我们想改回"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() 基本上设置为从a 字符串的end 开始查找每个"foo" 的索引。作为一个函数,这可能只是:

        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() 是否仍然可以使用。但是,我认为这是不可能的。建议使用正则表达式的另一个答案总是一个很好的调用。

        【讨论】:

          猜你喜欢
          • 2018-08-11
          • 2016-05-07
          • 2018-09-08
          • 1970-01-01
          • 2013-02-20
          • 2011-06-05
          • 1970-01-01
          • 2021-12-20
          • 1970-01-01
          相关资源
          最近更新 更多