rreplace - Como substituir a última ocorrência de uma expressão em uma string?

139

Existe uma maneira rápida no Python de substituir seqüências de caracteres, mas, em vez de começar do começo como no começo replace, do final? Por exemplo:

>>> def rreplace(old, new, occurrence)
>>>     ... # Code to replace the last occurrences of old by new

>>> '<div><div>Hello</div></div>'.rreplace('</div>','</bad>',1)
>>> '<div><div>Hello</div></bad>'
Barthelemy
fonte
5
Boa pergunta, a julgar pelas soluções complicadas para um problema tão simples.
Justin Ardini 31/03/10
3
Há uma resposta elegante nas respostas abaixo, que levou 9 anos (!) Para ser adicionada a essa pergunta, basta rolar para baixo para encontrá-la.
John D

Respostas:

196
>>> def rreplace(s, old, new, occurrence):
...  li = s.rsplit(old, occurrence)
...  return new.join(li)
... 
>>> s
'1232425'
>>> rreplace(s, '2', ' ', 2)
'123 4 5'
>>> rreplace(s, '2', ' ', 3)
'1 3 4 5'
>>> rreplace(s, '2', ' ', 4)
'1 3 4 5'
>>> rreplace(s, '2', ' ', 0)
'1232425'
mg.
fonte
9
Muito agradável! Em uma referência não científica de substituição da última ocorrência de uma expressão em uma sequência típica no meu programa (> 500 caracteres), sua solução foi três vezes mais rápida que a solução de Alex e quatro vezes mais rápida que a solução de Mark. Obrigado a todos por suas respostas!
Barthelemy
2
Obrigado, funciona. O .replacemétodo usa um terceiro argumento opcional 'count', que informa para substituir as primeiras n ocorrências. Não seria intuitivo se isso levasse um -1, mas infelizmente não será, então precisamos da sua solução.
cardamomo
17

Não vou fingir que essa é a maneira mais eficiente de fazer isso, mas é uma maneira simples. Ele inverte todas as strings em questão, executa uma substituição comum usando str.replaceas strings invertidas e depois reverte o resultado da maneira correta:

>>> def rreplace(s, old, new, count):
...     return (s[::-1].replace(old[::-1], new[::-1], count))[::-1]
...
>>> rreplace('<div><div>Hello</div></div>', '</div>', '</bad>', 1)
'<div><div>Hello</div></bad>'
Mark Byers
fonte
10

Aqui está uma frase:

result = new.join(s.rsplit(old, maxreplace))

Retorne uma cópia da string s com todas as ocorrências de substring antigas substituídas por novas . As primeiras ocorrências de maxreplace são substituídas.

e um exemplo completo disso em uso:

s = 'mississipi'
old = 'iss'
new = 'XXX'
maxreplace = 1

result = new.join(s.rsplit(old, maxreplace))
>>> result
'missXXXipi'
John D
fonte
1
Agradável! Utilizou-o para remover vírgulas finais das linhas: line = "".join(line.rsplit(",", 1))mantendo espaço de preenchimento posteriormente.
Neto
9

Apenas inverta a string, substitua a primeira ocorrência e inverta-a novamente:

mystr = "Remove last occurrence of a BAD word. This is a last BAD word."

removal = "BAD"
reverse_removal = removal[::-1]

replacement = "GOOD"
reverse_replacement = replacement[::-1]

newstr = mystr[::-1].replace(reverse_removal, reverse_replacement, 1)[::-1]
print ("mystr:", mystr)
print ("newstr:", newstr)

Resultado:

mystr: Remove last occurence of a BAD word. This is a last BAD word.
newstr: Remove last occurence of a BAD word. This is a last GOOD word.
Joe
fonte
5

Se você souber que a string 'antiga' não contém caracteres especiais, é possível fazer isso com uma regex:

In [44]: s = '<div><div>Hello</div></div>'

In [45]: import re

In [46]: re.sub(r'(.*)</div>', r'\1</bad>', s)
Out[46]: '<div><div>Hello</div></bad>'
Dave Kirby
fonte
1

Aqui está uma solução recursiva para o problema:

def rreplace(s, old, new, occurence = 1):

    if occurence == 0:
        return s

    left, found, right = s.rpartition(old)

    if found == "":
        return right
    else:
        return rreplace(left, old, new, occurence - 1) + new + right
naivnomore
fonte