rreplace - Comment remplacer la dernière occurrence d'une expression dans une chaîne?

139

Existe-t-il un moyen rapide en Python de remplacer les chaînes mais, au lieu de commencer par le début comme le replacefait, en commençant par la fin? Par exemple:

>>> 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>'
Barthélemy
la source
5
Bonne question, à en juger par les solutions compliquées à un problème aussi simple.
Justin Ardini
3
Il y a un élégant one-liner dans les réponses ci-dessous qui a pris 9 ans (!) Pour être ajouté à cette question, faites simplement défiler vers le bas pour le trouver.
John D le

Réponses:

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.
la source
9
Très agréable! Dans un benchmark non scientifique de remplacement de la dernière occurrence d'une expression dans une chaîne typique de mon programme (> 500 caractères), votre solution était trois fois plus rapide que la solution d'Alex et quatre fois plus rapide que la solution de Mark. Merci à tous pour vos réponses!
Barthelemy
2
Merci, ça marche. La .replaceméthode prend un troisième argument optionnel 'count' qui lui dit de remplacer les n premières occurrences. N'aurait-il pas été intuitif si cela prendrait un -1 mais malheureusement ce ne sera pas le cas, nous avons donc besoin de votre solution.
cardamome
17

Je ne vais pas prétendre que c'est la manière la plus efficace de le faire, mais c'est une manière simple. Il inverse toutes les chaînes en question, effectue un remplacement ordinaire en utilisant str.replaceles chaînes inversées, puis inverse le résultat dans le bon sens:

>>> 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
la source
10

Voici un one-liner:

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

Renvoie une copie de la chaîne s avec toutes les occurrences de la sous-chaîne old remplacées par new . Les premières occurrences de maxreplace sont remplacées.

et un exemple complet de ceci en cours d'utilisation:

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

result = new.join(s.rsplit(old, maxreplace))
>>> result
'missXXXipi'
John D
la source
1
Agréable! Utilisé pour supprimer les virgules de fin des lignes: line = "".join(line.rsplit(",", 1))tout en gardant l'espace de remplissage par la suite.
petit
9

Inversez simplement la chaîne, remplacez la première occurrence et inversez-la à nouveau:

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)

Production:

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
la source
5

Si vous savez que l'ancienne chaîne ne contient aucun caractère spécial, vous pouvez le faire avec une expression régulière:

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
la source
1

Voici une solution récursive au problème:

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
la source