rreplace - Jak zamienić ostatnie wystąpienie wyrażenia w ciągu?

139

Czy w Pythonie istnieje szybki sposób na zastąpienie ciągów, ale zamiast zaczynać od początku, jak to się replacedzieje, zaczynając od końca? Na przykład:

>>> 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
źródło
5
Dobre pytanie, sądząc po skomplikowanych rozwiązaniach tak prostego problemu.
Justin Ardini
3
W poniższych odpowiedziach znajduje się elegancka jedna linijka, której dodanie do tego pytania zajęło 9 lat (!), Po prostu przewiń w dół, aby ją znaleźć.
John D

Odpowiedzi:

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.
źródło
9
Bardzo dobrze! W nienaukowym benchmarku polegającym na zamianie ostatniego wystąpienia wyrażenia w typowym ciągu w moim programie (> 500 znaków), Twoje rozwiązanie było trzy razy szybsze niż rozwiązanie Alexa i cztery razy szybsze niż rozwiązanie Marka. Dziękuję wszystkim za odpowiedzi!
Barthelemy
2
Dzięki, działa. .replaceMetoda ta trzecia opcjonalny argument „licznik”, który informuje go zastąpić n pierwszych zdarzeń. Czy nie byłoby to intuicyjne, gdyby wymagało to -1, ale niestety nie, więc potrzebujemy Twojego rozwiązania.
kardamon
17

Nie zamierzam udawać, że jest to najefektywniejszy sposób, ale jest to prosty sposób. Odwraca wszystkie odnośne ciągi, wykonuje zwykłą zamianę, używając str.replaceodwróconych ciągów, a następnie odwraca wynik z powrotem we właściwą stronę:

>>> 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
źródło
10

Oto jedna linijka:

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

Zwraca kopię łańcucha s ze wszystkimi wystąpieniami podłańcucha starego zamienionego na nowy . Pierwsze wystąpienia maxreplace są zastępowane.

i pełny przykład tego w użyciu:

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

result = new.join(s.rsplit(old, maxreplace))
>>> result
'missXXXipi'
John D.
źródło
1
Miły! Użył go do usunięcia końcowych przecinków z wierszy: line = "".join(line.rsplit(",", 1))zachowując później spację dopełnienia.
wnuczka
9

Po prostu odwróć ciąg, zamień pierwsze wystąpienie i odwróć je ponownie:

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)

Wynik:

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
źródło
5

Jeśli wiesz, że „stary” ciąg nie zawiera żadnych znaków specjalnych, możesz to zrobić za pomocą wyrażenia regularnego:

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
źródło
1

Oto rekurencyjne rozwiązanie problemu:

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
naiwność
źródło