Пронумеровать найденные совпадения

открываю файл в цикле и построчно прохожусь по нему заменяя регулярными выражениями необходимый текст

for i in f:
 line = re.sub(r'#.{1,}?#', r'<>\g<0></>', line )

Как можно сделать, что бы следующий найденный элемент заменялся числом на 1 больше? т.е. примерно так:

line = re.sub(r'#.{1,}?#', r'<1>\g<0></1>', line )
line = re.sub(r'#.{1,}?#', r'<2>\g<0></2>', line )
line = re.sub(r'#.{1,}?#', r'<3>\g<0></3>', line )

как я понимаю каждый раз когда находится совпадение нужно добавлять в переменную +1 но как написать такую проверку мне не совсем понимаю


Ответы (2 шт):

Автор решения: Михаил Муругов

Что-то про enumerate все забыли:

for idx, line in enumerate(f):
    line = re.sub(r'#.+?#', rf'<{idx}>\g<0></{idx}>', line)

В Вашей регулярке заменил {1,} на +, т.к. это одно и то же.

Но, как отметил insolor, Ваш подход не совсем верен, если задача: "что бы следующий найденный элемент заменялся числом на 1 больше", т.к. при Вашем подходе у элементов будет тег не таким, каким по счёту нашелся элемент, а таким, в какой по счёту строке он нашёлся.


Если же решать задачу "что бы следующий найденный элемент заменялся числом на 1 больше", то предлагаю такой вариант:

class CounterReplacer:
    def __init__(self, start=0, step=1):
        self._counter = start
        self._step = step

    def replace(self, match: re.Match):
        i = self._counter
        self._counter += self._step
        return f'<{i}>{match[0]}</{i}>'


# ...

replacer = CounterReplacer(start=1)
for line in f:
    line = re.sub(r'#.+?#', replacer.replace, line)
→ Ссылка
Автор решения: Wiktor Stribiżew

В начале совет: не используйте {1,}, это "длинная версия" квантификатора +. Чтобы захватить пустые строки между двух # не используйте .+?, используйте #[^#]*#, иначе, если в строке будут пустые ## совпадения будут начинаться со второго, конечного, символа.

Чтобы решить поставленную задачу, добавте лямбду в качестве аргумента замены и передайте в него counter:

import re
from itertools import count

line = r'#Совп. А# ... #Совп. Б# ... #Совп. В#'
counter = count(1)
line = re.sub(r'#[^#]*#', lambda x: '<{0}>{1}</{0}>'.format(str(next(counter)),x.group()), line)
print(line)
# => <1>#Совп. А#</1> ... <2>#Совп. Б#</2> ... <3>#Совп. В#</3>

См. пример работы кода на Python.

В данном случае регулярное выражение находит и заменяет совпадения за один проход.

Есть ещё один вариант без дополнительного import, использовать вспомогательный метод, чтобы можно было передать с помощью лямбды 2 аргумена (найденный объект MatchData и счётчик):

import re

line = r'#Совп. А# ... #Совп. Б# ... #Совп. В#'

def repl(x, count):
    count.i += 1
    return '<{0}>{1}</{0}>'.format(str(count.i),x.group())
 
obj = lambda:None
obj.i = 0
line = re.sub(r'#[^#]*#', lambda x: repl(x, obj), line)
print(line)
# => <1>#Совп. А#</1> ... <2>#Совп. Б#</2> ... <3>#Совп. В#</3>

См. ещё один пример на Python.

→ Ссылка