Замена текста в одном и том же файле в цикле

Рейтинг: 0Ответов: 3Опубликовано: 10.03.2023

Всем привет!

Есть следующая задачка. У меня есть файл, где имеется следующий текст:

`<!-- replace text here-->`
...
`<!-- replace text here-->`
...
`<!-- replace text here-->`
...
`<!-- replace text here-->`
...
`<!-- replace text here-->`

Я хочу с помощью цикла заменить эти значения на следующее:

`<!-- replace text here 0-->`
...
`<!-- replace text here 1-->`
...
`<!-- replace text here 2-->`
...
`<!-- replace text here 3-->`
...
`<!-- replace text here 4-->`

Где цифры - номер цикла. Для этого я написал следующий код:

for r in range (5):
    with open(path_to_html, "r+") as f:
        contents = f.read()
        contents = re.sub(r'<!-- replace text here-->', '<!-- replace text here '+str(r)+'-->', contents)
        f.write(contents)
        f.truncate()
        f.close()

Но по каким-то неведомым мне причинам я получаю следующий результат:

`<!-- replace text here 0-->`
...
`<!-- replace text here 0-->`
...
`<!-- replace text here 0-->`
...
`<!-- replace text here 0-->`
...
`<!-- replace text here 0-->`

Подскажите, пожалуйста, что я делаю не так?

Ответы

▲ 2

Почему по неведомым причинам?

На нулевой итерации у вас данная подстрока <!-- replace text here--> заменяется на <!-- replace text here 0--> во всех случаях.

На первой итерации у вас уже нет подстроки со значением <!-- replace text here--> в файле, поэтому ничего не меняется и не происходит. И так далее со всеми итерациями дальше.

Что можно сделать?

Поскольку существует метод replace для строк из коробки, я бы его и использовал, поскольку функционала достаточно для решения подобной задачи

for r in range (5):
    with open(path_to_html, "r+") as f:
        contents = f.read()
        contents = contents.replace(r'<!-- replace text here-->', '<!-- replace text here '+str(r)+'-->', 1)
        f.write(contents)
        f.truncate()
        f.close()

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

Метод replace(old, new, count), принимает 2 обязательных параметра (old, new) и один опциональный (count). В count мы передаем количество вхождений , которые нам необходимо заменить. Вам нужно менять только одно вхождение за одну итерацию, поэтому ставите еденичку и радуетесь результату

▲ 0

Можно так:

with open(path_to_html, "r") as f:
    contents = f.read()

with open(path_to_html, "w") as f:
    for r in range(5):
        contents = re.sub(r'<!-- replace text here-->', '<!-- replace text here '+str(r)+'-->', contents, count=1)
    f.write(contents)
    f.truncate()
▲ 0

Присоединяюсь к замечанию Дмитрия - не гоже пять раз подряд открывать и закрывать файл. А если это будет 500 раз и более?

Ещё странным выглядит помещать f.truncate() после f.write(contents). Это не приведет к перезаписи файла. Новый текст будет добавлен в конец файла.

Предлагаю свой вариант:

# Открываем файл только для чтения
# и быстро считываем содержимое в локальную переменную
with open(path_to_html, 'r') as f:
    # для больших файлов не лучшая идея читать файл целиком в память
    content = f.read()

# оператор with сам закроет файл автоматически
# далее работаем с текстом в локальной переменной - content
search_text = '<!-- replace text here-->'
r = 0
# если требуется ограничить замену конкретного числа строк (max_count), то пишем:
# while search_text in content and r <= max_count:
# пока существует искомая фраза, заменяем текст (по одной итерации) на новый с очередным счетчиком
while search_text in content:
    content = content.replace(search_text, '<!-- replace text here '+str(r)+'-->', 1)
    r += 1

# снова открываем файл, но уже только для записи и обновляем весь файл целиком
with open(path_to_html, 'w') as f:
    f.write(content)

Плюс предложенного варианта: Операции с файлом ограничены двумя короткими операциями. Во время работы "тяжелого" поиска текста, файл остается закрытым.

Можно доработать для работы с большими файлами - построчное чтение. Если переменную search_text заменить списком строк и доработать цикл while, то возможно искать любое сочетание текста.