48

I want to replace characters at the end of a python string. I have this string:

s = "123123"

I want to replace the last 2 with x. Suppose there is a method called replace_last:

>>> replace_last(s, '2', 'x')
'1231x3'

Is there any built-in or easy method to do this?


It's similar to python's str.replace():

>>> s.replace('2', 'x', 1)
'1x3123'

But it's from the end to beginning.

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Freewind
  • 193,756
  • 157
  • 432
  • 708

10 Answers10

52

Using regular expression function re.sub to replace words at end of string

import re
s = "123123"
s = re.sub('23$', 'penguins', s)
print s

Prints:

1231penguins

or

import re
s = "123123"
s = re.sub('^12', 'penguins', s)
print s

Prints:

penguins3123
JamesThomasMoon
  • 6,169
  • 7
  • 37
  • 63
Tony Ingraldi
  • 537
  • 3
  • 2
  • For the example given in the question: `r = re.sub(r"2[^2]$", "x", s)` – Eric Mar 25 '15 at 17:46
  • 1
    the question is not asking about the ending of a string rather replacement from the back of the string. – Mina Jun 04 '20 at 18:35
  • @Eric That deletes the last 3. Use this: `re.sub(r"2([^2])$", r"x\1", s)`. Although in any case, this wouldn't work for multi-character replacements due to the character class. – wjandrea Dec 17 '22 at 20:02
45

This is exactly what the rpartition function is used for:

S.rpartition(sep) -> (head, sep, tail)

Search for the separator sep in S, starting at the end of S, and return the part before it, the separator itself, and the part after it. If the separator is not found, return two empty strings and S.

I wrote this function showing how to use rpartition in your use case:

def replace_last(source_string, replace_what, replace_with):
    head, _sep, tail = source_string.rpartition(replace_what)
    return head + replace_with + tail

s = "123123"
r = replace_last(s, '2', 'x')
print r

Output:

1231x3
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Mizipzor
  • 51,151
  • 22
  • 97
  • 138
  • 2
    This solution is flawed. If the replace_what string is not found, the replace_with be inserted at the beginning of the string. – Jean-Marc S. Sep 21 '21 at 21:45
16

This is one of the few string functions that doesn't have a left and right version, but we can mimic the behaviour using some of the string functions that do.

>>> s = '123123'
>>> t = s.rsplit('2', 1)
>>> u = 'x'.join(t)
>>> u
'1231x3'

or

>>> 'x'.join('123123'.rsplit('2', 1))
'1231x3'
Chris Adams
  • 1,067
  • 8
  • 15
  • 4
    +1: As well as being shorter than the accepted solution, this solution has the advantage that one can make as many replacements from the right as one wishes, just by changing the value of the second parameter in `rsplit()`. This was an advantage to me when I went looking for a solution to this problem because I would otherwise have had to use a loop to make multiple replacements. – Simon Dec 17 '13 at 02:25
  • The only problem I can think of is if you pass `sep=None` and `s` contains multiple whitespace characters, then it doesn't behave like a replacement per se. For example: `s = "1\n\n1"`, we have `s.rsplit(None, 1) == ['1', '1']` vs `s.rsplit('\n', 1) == ['1\n', '1']`. – wjandrea Dec 17 '22 at 21:58
8
>>> s = "aaa bbb aaa bbb"
>>> s[::-1].replace('bbb','xxx',1)[::-1]
'aaa bbb aaa xxx'

For your second example

>>> s = "123123"
>>> s[::-1].replace('2','x',1)[::-1]
'1231x3'
Sepheus
  • 151
  • 2
  • 2
    this works only on palindromes, fix: source[::-1].replace(old[::-1], new[::-1], count)[::-1] – Kresimir Apr 16 '20 at 11:28
  • @Kresimir -- good catch (and fix) on the palindromes issue. two thumbs up! (Personally I like this [::-1] method best because can use `replace()` normal sytax for both forward and reverse replace). – Daniel Goldfarb Jan 08 '21 at 01:25
5

When the wanted match is at the end of string, re.sub comes to the rescue.

>>> import re
>>> s = "aaa bbb aaa bbb"
>>> s
'aaa bbb aaa bbb'
>>> re.sub('bbb$', 'xxx', s)
'aaa bbb aaa xxx'
>>> 
gimel
  • 83,368
  • 10
  • 76
  • 104
2

Here is a solution based on a simplistic interpretation of your question. A better answer will require more information.

>>> s = "aaa bbb aaa bbb"
>>> separator = " "
>>> parts = s.split(separator)
>>> separator.join(parts[:-1] + ["xxx"])
'aaa bbb aaa xxx'

Update

(After seeing edited question) another very specific answer.

>>> s = "123123"
>>> separator = "2"
>>> parts = s.split(separator)
>>> separator.join(parts[:-1]) + "x" + parts[-1]
'1231x3'

Update 2

There is far better way to do this. Courtesy @mizipzor.

Community
  • 1
  • 1
Manoj Govindan
  • 72,339
  • 21
  • 134
  • 141
  • The update has the same problem as Mizipzor's answer: If `separator` is not found, the replacement will get inserted at the start of the string. – wjandrea Dec 17 '22 at 22:21
1

There is a way to handle replace to make it work backwards. The idea is to reverse the string, text to replace and new text using [::-1]. The logic stays the same.

For example:

>>> a = "123 456 123 456 123 456"

and we want to replace the last one, (or two, or n) occurrences of "123" with "abc"

>>> a[::-1].replace("123"[::-1], "abc"[::-1], 1)[::-1]
'123 456 123 456 abc 456'

This could be put as a function that behaves exactly like replace.

def replace_right(text, old, new, count=-1):
    """
    Return a copy of text with all occurrences of substring old
    replaced by new, starting from the right.

    If count is given and is not -1, only the first count
    occurrences are replaced.
    """
    return text[::-1].replace(old[::-1], new[::-1], count)[::-1]

Execution:

>>> replace_right(a, "123", "abc", 1)
'123 456 123 456 abc 456'

>>> replace_right(a, "123", "abc", 2)
'123 456 abc 456 abc 456'
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Taha
  • 709
  • 5
  • 10
0

I got a tricky answer, but it is not efficient enough

fname = '12345.png.pngasdfg.png'
suffix = '.png'
fname_rev = fname[::-1]
suffix_rev = suffix[::-1]
fullname_rev = fname_rev.replace(suffix_rev, '', 1)
fullname = fullname_rev[::-1]
fullname '12345.png.pngasdfg'

Built-in function replace() takes three arguments str.replace(old, new, max_time) So you can delete the last mached string from the origional string

wjandrea
  • 28,235
  • 9
  • 60
  • 81
Yiling Liu
  • 666
  • 1
  • 6
  • 21
  • You'd need to reverse the `new` argument as well. See [Taha's answer](/a/67457853/4518341) for a full implementation. – wjandrea Dec 17 '22 at 22:16
  • Why do you say it's "not efficient enough"? In CPython, string methods are implemented in C, so they should be very fast. Only very large strings would have significant impact, no? – wjandrea Dec 17 '22 at 22:17
0

There is a very easy way to do this, follow my steps

s = "stay am stay am  delete am"
#  want to remove the last am
s = s[:s.rfind('am')]
print(s)

Here at first, we find the index number of am's last occurrence, then take 0th index to that particular index.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • 1
    Thanks, but it's not about "remove", but "replace" – Freewind Aug 29 '21 at 12:02
  • Hii @Freewind After removing you can concatenate any string at that position CODE `str = "stay am stay am delete vvamvv gggg" # want to remove the last am str = str[:str.rfind('am')] + "REPLACED" + str[(str.rfind('am') + len("am")):] print(str)` – Oishik Sinha Sep 01 '21 at 04:51
  • `str` is a bad variable name since it [shadows](https://en.wikipedia.org/wiki/Variable_shadowing) the [builtin `str` type](https://docs.python.org/3/library/stdtypes.html#str). It's better to use a more descriptive name, or at least something like `s`. I fixed it for you :) Cf. [TypeError: 'list' object is not callable](/q/31087111/4518341). – wjandrea Dec 17 '22 at 20:08
-1

For the second example, I would recommend rsplit, as it is very simple to use and and directly achieves the goal. Here is a little function with it:

def replace_ending(sentence, old, new):
    if sentence.endswith(old):
        i = sentence.rsplit(old,1)
        new_sentence =new.join(i)
        return new_sentence
    return sentence

print(replace_ending("aaa bbb aaa bbb", "bbb", "xxx")) 

Output:

aaa bbb aaa xxx

double-beep
  • 5,031
  • 17
  • 33
  • 41
  • Does this add anything that [Chris Adams's answer](/a/3679215/4518341) doesn't already? I'm not sure why you're using `sentence.endswith()` given the example in the question, which doesn't end with the substring to be replaced. – wjandrea Dec 17 '22 at 22:12