如何在大海捞针中取代针的第N个外观? (蟒蛇)

时间:2011-08-24 21:31:01

标签: python regex replace

我正在尝试替换大海捞针的第N个外观。我想通过re.sub()简单地做到这一点,但似乎无法想出一个合适的正则表达式来解决这个问题。我想尝试改编:http://docstore.mik.ua/orelly/perl/cookbook/ch06_06.htm但是我想在跨越多线时失败了。

我当前的方法是一种迭代方法,它在每次突变后从头开始查找每个事件的位置。这是非常低效的,我想得到一些输入。谢谢!

6 个答案:

答案 0 :(得分:3)

我认为你的意思是re.sub。您可以传递一个函数并跟踪到目前为止调用它的频率:

def replaceNthWith(n, replacement):
    def replace(match, c=[0]):
        c[0] += 1
        return replacement if c[0] == n else match.group(0)
    return replace

用法:

re.sub(pattern, replaceNthWith(n, replacement), str)

但是这种方法感觉有点hacky,也许有更优雅的方式。

DEMO

答案 1 :(得分:2)

像这样的正则表达式应该可以帮到你。虽然我不确定它的效率如何:

#N=3   
re.sub(
  r'^((?:.*?mytexttoreplace){2}.*?)mytexttoreplace',
  '\1yourreplacementtext.', 
  'mystring',
  flags=re.DOTALL
)

DOTALL标志很重要。

答案 2 :(得分:1)

你可以使用带有MatchObject.start()和MatchObject.end()的re.findall吗?

使用.findall查找字符串中所有模式的出现,使用.start / .end获取第N次出现的索引,使用索引创建具有替换值的新字符串?

答案 3 :(得分:1)

我一直在苦苦挣扎,但是我发现了一个我认为非常pythonic的解决方案:

>>> def nth_matcher(n, replacement):
...     def alternate(n):
...         i=0
...         while True:
...             i += 1
...             yield i%n == 0
...     gen = alternate(n)
...     def match(m):
...         replace = gen.next()
...         if replace:
...             return replacement
...         else:
...             return m.group(0)
...     return match
...     
... 
>>> re.sub("([0-9])", nth_matcher(3, "X"), "1234567890")
'12X45X78X0'

编辑:匹配器由两部分组成:

  1. alternate(n)功能。这将返回generator,返回无限序列True / False,其中每第n个值为True。可以将其想象为list(alternate(3)) == [False, False, True, False, False, True, False, ...]

  2. match(m)功能。这是传递给re.sub的函数:它获取alternate(n)gen.next())中的下一个值,如果它是True,它将替换匹配的值;否则,它保持不变(用它自己替换)。

  3. 我希望这很清楚。如果我的解释是模糊的,请说出来,我会改进它。

答案 4 :(得分:0)

如果模式("针")或替换是一个复杂的正则表达式,你就不能假设任何东西。函数" nth_occurrence_sub"我提出的是一个更通用的解决方案:

def nth_match_end(pattern, string, n, flags):
    for i, match_object in enumerate(re.finditer(pattern, string, flags)):
        if i + 1 == n:
            return match_object.end()


def nth_occurrence_sub(pattern, repl, string, n=0, flags=0):
    max_n = len(re.findall(pattern, string, flags))
    if abs(n) > max_n or n == 0:
        return string
    if n < 0:
        n = max_n + n + 1
    sub_n_times = re.sub(pattern, repl, string, n, flags)
    if n == 1:
        return sub_n_times
    nm1_end = nth_match_end(pattern, string, n - 1, flags)
    sub_nm1_times = re.sub(pattern, repl, string, n - 1, flags)
    sub_nm1_change = sub_nm1_times[:-1 * len(string[nm1_end:])]
    components = [
        string[:nm1_end],
        sub_n_times[len(sub_nm1_change):]
        ]
    return ''.join(components)

答案 5 :(得分:0)

我有一个类似的功能,我写这样做。我试图复制SQL REGEXP_REPLACE()功能。我最终得到了:

def sql_regexp_replace( txt, pattern, replacement='', position=1, occurrence=0, regexp_modifier='c'):
    class ReplWrapper(object):
        def __init__(self, replacement, occurrence):
            self.count = 0
            self.replacement = replacement
            self.occurrence = occurrence
        def repl(self, match):
            self.count += 1
            if self.occurrence == 0 or self.occurrence == self.count:
                return match.expand(self.replacement)
            else: 
                try:
                    return match.group(0)
                except IndexError:
                    return match.group(0)
    occurrence = 0 if occurrence < 0 else occurrence
    flags = regexp_flags(regexp_modifier)
    rx = re.compile(pattern, flags)
    replw = ReplWrapper(replacement, occurrence)
    return txt[0:position-1] + rx.sub(replw.repl, txt[position-1:])

我还没有看到提到的一个重要注意事项是,您需要返回match.expand(),否则它无法正确扩展\1模板并将其视为文字。

如果你希望这个工作,你需要以不同的方式处理标志(或从my github获取它,它很容易实现,你可以通过设置它来为它进行虚拟处理到0并忽略我对regexp_flags()的号召。