rreplace - ¿Cómo reemplazar la última aparición de una expresión en una cadena?

139

¿Hay una manera rápida en Python para reemplazar cadenas pero, en lugar de comenzar desde el principio como lo replacehace, comenzando desde el final? Por ejemplo:

>>> 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
fuente
55
Buena pregunta, a juzgar por las soluciones complicadas a un problema tan simple.
Justin Ardini
3
Hay una respuesta elegante en las respuestas a continuación que tardó 9 años (!) En agregarse a esta pregunta, simplemente desplácese hacia abajo para encontrarla.
John D

Respuestas:

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.
fuente
9
¡Muy agradable! En un punto de referencia no científico de reemplazar la última aparición de una expresión en una cadena típica en mi programa (> 500 caracteres), su solución fue tres veces más rápida que la solución de Alex y cuatro veces más rápida que la solución de Mark. ¡Gracias a todos por sus respuestas!
Barthelemy
2
Gracias funciona. El .replacemétodo toma un tercer argumento opcional 'cuenta' que le dice que reemplace las primeras n apariciones. No hubiera sido intuitivo si eso tomara un -1, pero desafortunadamente no lo hará, por lo que necesitamos su solución.
cardamomo
17

No voy a pretender que esta es la forma más eficiente de hacerlo, pero es una forma simple. Invierte todas las cadenas en cuestión, realiza un reemplazo ordinario utilizando str.replacelas cadenas invertidas, luego invierte el resultado de la manera correcta:

>>> 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
fuente
10

Aquí hay una frase:

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

Devuelve una copia de la cadena s con todas las apariciones de la subcadena antigua reemplazada por nueva . Los primeros casos de reemplazo máximo se reemplazan.

y un ejemplo completo de esto en uso:

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

result = new.join(s.rsplit(old, maxreplace))
>>> result
'missXXXipi'
John D
fuente
1
¡Agradable! Lo usó para eliminar las comas finales de las líneas: line = "".join(line.rsplit(",", 1))manteniendo el espacio de relleno después.
nieto
9

Simplemente invierta la cadena, reemplace la primera aparición y vuelva a invertirla:

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)

Salida:

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
fuente
5

Si sabe que la cadena 'antigua' no contiene ningún carácter especial, puede hacerlo con una expresión regular:

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
fuente
1

Aquí hay una solución recursiva al 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
fuente