Как заменить N-е появление иголки в стоге сена?(Python) - PullRequest
2 голосов
/ 25 августа 2011

Я пытаюсь заменить N-е появление иголки в стоге сена.Я хочу сделать это просто с помощью re.sub (), но, похоже, не могу найти подходящее регулярное выражение для решения этой проблемы.Я пытаюсь адаптировать: http://docstore.mik.ua/orelly/perl/cookbook/ch06_06.htm, но мне не удается охватить несколько строк, я полагаю.

Мой текущий метод - это итеративный подход, который находит положение каждого вхождения с начала после каждой мутации.Это довольно неэффективно, и я хотел бы получить некоторую информацию.Спасибо!

Ответы [ 6 ]

3 голосов
/ 25 августа 2011

Я думаю, что вы имеете в виду 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)

Но этот подход выглядит немного странно, может быть, есть более элегантныйпути.

DEMO

2 голосов
/ 25 августа 2011

Что-то вроде этого регулярного выражения должно помочь вам. Хотя я не уверен, насколько это эффективно:

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

Флаг DOTALL важен.

1 голос
/ 25 августа 2011

Я некоторое время боролся с этим, но я нашел решение, которое я считаю довольно питонным:

>>> 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). Это возвращает генератор , который возвращает бесконечную последовательность True / False, где каждое n-е значение - True. Думайте об этом как list(alternate(3)) == [False, False, True, False, False, True, False, ...].

  2. Функция match(m). Эта функция передается в re.sub: она получает следующее значение в alternate(n) (gen.next()) и, если это True, заменяет соответствующее значение; в противном случае он сохраняет его неизменным (заменяет собой).

Надеюсь, это достаточно ясно. Если мое объяснение туманно, скажите, и я его улучшу.

1 голос
/ 25 августа 2011

Не могли бы вы сделать это, используя re.findall с MatchObject.start () и MatchObject.end ()?

найти все вхождения шаблона в строке с помощью .findall, получить индексы N-го вхождения с помощью .start /.end, сделать новую строку со значением замены, используя индексы?

0 голосов
/ 08 декабря 2015

У меня есть похожая функция, которую я написал для этого. Я пытался повторить функциональность 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()).

0 голосов
/ 02 июля 2015

Если шаблон («игла») или замена представляет собой сложное регулярное выражение, вы ничего не можете предположить.Функция "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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...