537

What is the easiest way in Python to replace a character in a string?

For example:

text = "abcdefg";
text[1] = "Z";
           ^
martineau
  • 119,623
  • 25
  • 170
  • 301
kostia
  • 6,161
  • 3
  • 19
  • 23

15 Answers15

730

Don't modify strings.

Work with them as lists; turn them into strings only when needed.

>>> s = list("Hello zorld")
>>> s
['H', 'e', 'l', 'l', 'o', ' ', 'z', 'o', 'r', 'l', 'd']
>>> s[6] = 'W'
>>> s
['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']
>>> "".join(s)
'Hello World'

Python strings are immutable (i.e. they can't be modified). There are a lot of reasons for this. Use lists until you have no choice, only then turn them into strings.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
scvalex
  • 14,931
  • 2
  • 34
  • 43
  • 15
    Those looking for speed/efficiency, [read this](http://stackoverflow.com/a/22149018/4334743) – AneesAhmed777 Apr 19 '17 at 20:27
  • 28
    "Don't modify strings." why – hacksoi Oct 10 '18 at 21:14
  • 6
    "Create->modify->serialize->assign->free" more efficent than s[6]='W'? Hmm... Why other languages allow it, in spite of that "lot" of reasons? Interesting how a strange design can be defended (for love I suppose). Why not suggest adding a function MID(strVar,index,newChar) to Python core that direct accesses the char memory position, instead of unnecesarily byte shuffling with the whole string? – oscar Nov 27 '18 at 18:37
  • 3
    @hacksoi, @oscar, the reason is quite simple: no need to refcount when passing pointers around to implement copy-on-modify, or outright copy the whole string in case someone wants to modify that string - this leads to speed increase in generic use. There is no need for things like `MID` due to slices: `s[:index] + c + s[index+1:]` – MultiSkill Dec 04 '18 at 08:19
  • 2
    @oscar It is not for a "strange design" but for the fact that python distinguishes between unicode and byte strings, which is great, because users of "dumb" languages tend to just ignore the existence of different encodings, which results in a lot of trouble later on. – Bachsau Apr 15 '19 at 16:21
  • @MultiSkill: Splitting means lower performance (reserve new block of memory + copy both 2 slices inside = hundreds of CPU clock cycles, maybe thousands); while "MID" would require **just** writing 2 bytes (1 clock cycle!) for a unicode char. I think fast CPU's make us forget about what's really happening behind the scenes. – oscar Jul 06 '19 at 11:35
  • 2
    @oscar By dumb languages I mean they do not deal with unicode unless you explicitly tell them do. Of course you can write unicode capable applications in C. But you have to care about it all the time and need to test it explicitly to avoid trouble. Everything is machine-oriented. I worked with PHP before learning Python, and that language is a total mess. Regarding your note on fast CPUs I'm totally with you. But a part of that problem is the popular disapproval of premature optimization, which leads to slow interpreters and libraries by leaking lots of CPU cycles on the way. – Bachsau Jul 07 '19 at 13:03
  • 2
    @oscar, yes, strings in Python are immutable, so you have to reserve a new block of memory and copy all the data you need there in any case. But you're not wasting as many cycles as you claim. Most use cases do not require you to modify strings, but it does require you passing strings around a lot, so having immutable strings is really reasonable. There are other data structures you can use if you need to mutate its contents, and there are other languages that are closer to the metal if that is your thing. – MultiSkill Jul 07 '19 at 20:22
  • 6
    The link in the answer is dead. – antonagestam Mar 02 '21 at 09:44
  • This link will give some idea about why strings are immutable https://www.educative.io/edpresso/why-are-strings-in-python-immutable – Krishnadas PC Apr 30 '21 at 16:42
302

Fastest method?

There are three ways. For the speed seekers I recommend 'Method 2'

Method 1

Given by this answer

text = 'abcdefg'
new = list(text)
new[6] = 'W'
''.join(new)

Which is pretty slow compared to 'Method 2'

timeit.timeit("text = 'abcdefg'; s = list(text); s[6] = 'W'; ''.join(s)", number=1000000)
1.0411581993103027

Method 2 (FAST METHOD)

Given by this answer

text = 'abcdefg'
text = text[:1] + 'Z' + text[2:]

Which is much faster:

timeit.timeit("text = 'abcdefg'; text = text[:1] + 'Z' + text[2:]", number=1000000)
0.34651994705200195

Method 3:

Byte array:

timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)
1.0387420654296875
Community
  • 1
  • 1
Mehdi Nellen
  • 8,486
  • 4
  • 33
  • 48
  • 3
    Would be interesting to see how it fares against the bytearray method too. – gaborous Mar 07 '15 at 20:47
  • 3
    Good suggestion. The bytearray method is also slower: `timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)` twice as slow as the fastest one. – Mehdi Nellen Mar 09 '15 at 12:54
  • Thank you Mehdi for the test. I tried on my computer: bytearray is three times slower than the fastest method (method 2), but faster than list (which is five times slower than fastest method). So bytearray is not the fastest, but it's still a better alternative to list for more complicated manipulations on strings. – gaborous Mar 09 '15 at 17:26
  • 4
    Appreciate the tests, which make me rethink how I should manipulate Python strings. –  Feb 23 '16 at 06:07
  • 2
    Nice. Please Edit the answer to include method 3 too (bytearray). – AneesAhmed777 Apr 19 '17 at 20:25
  • 2
    It should be noted that most of the time here is spent in the conversions... (string -> byte array). If you have many edits to make to the string, then the byte array method will be faster. – Ian Sudbery Oct 30 '18 at 11:25
  • Just curious, how time is counted here, in seconds? So tha the first example takes a second? – VP. Mar 20 '20 at 10:16
  • @VictorPolevoy yes the return value is a float in seconds, it is dependent on computation speed so may vary from machine to machine and also from moment to moment – Mehdi Nellen Mar 20 '20 at 11:27
  • Will the first method still be slower than the second if we assume that we will only convert the string to list once and join everything after a great number of substitutions? Your test seems to convert the whole string to list each time and in this single substitution case it may be taking longer than the second method simply because that initial operation costs more. For a lot of substitutions I would expect the method that uses '+' to take longer than changing the elements in a list at each index. – Targaryel May 22 '20 at 18:50
  • 2
    Method 3 don't work with UTF8-Strings, that contain non-ASCII-Chars (Char/Byte-Value above 127), b/c allot of Char are encoded in more then 1 Byte. – Lutz L. Jun 18 '21 at 11:09
  • This may not work for everyone's use case, but text.replace('b','Z',1) is fractionally faster than Method 2 described when tested on my station. 0.1318 vs 0.1391. – horta Jul 07 '21 at 14:43
  • **Bytes are not text**. The bytearray method should not be considered a real solution. Python 3 forced people to deal with text **correctly**, and had **very good reasons** for forcing people to do so. The time for complaints has long passed, not that I was ever sympathetic to them. – Karl Knechtel Aug 07 '22 at 06:59
  • `timeit.timeit("text = 'abcdefg'; text = f'{text[:1]}Z{text[2:]}'", number=1000000)` is even faster - 0.1772725999999949 seconds – Superdooperhero Aug 19 '22 at 19:48
152
new = text[:1] + 'Z' + text[2:]
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
56

Python strings are immutable, you change them by making a copy.
The easiest way to do what you want is probably:

text = "Z" + text[1:]

The text[1:] returns the string in text from position 1 to the end, positions count from 0 so '1' is the second character.

edit: You can use the same string slicing technique for any part of the string

text = text[:1] + "Z" + text[2:]

Or if the letter only appears once you can use the search and replace technique suggested below

guntbert
  • 536
  • 6
  • 19
Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • I ment the 2nd character, IE. the character at place number 1 (as apposed to the 1st character, number 0) – kostia Aug 04 '09 at 15:56
14

Starting with python 2.6 and python 3 you can use bytearrays which are mutable (can be changed element-wise unlike strings):

s = "abcdefg"
b_s = bytearray(s)
b_s[1] = "Z"
s = str(b_s)
print s
aZcdefg

edit: Changed str to s

edit2: As Two-Bit Alchemist mentioned in the comments, this code does not work with unicode.

Mahmoud
  • 539
  • 4
  • 6
  • 2
    This answer is incorrect. For one thing, it should be `bytearray(s)`, not `bytearray(str)`. For another, this will produce: `TypeError: string argument without an encoding`. If you specify an encoding, then you get `TypeError: an integer is required`. That's with Python 3 or Python 2's unicode. If you do this in Python 2 (with a corrected second line), it won't work for non-ASCII characters because they may not be just one byte. Try it with `s = 'Héllo'` and you will get `'He\xa9llo'`. – Two-Bit Alchemist Nov 20 '15 at 21:58
  • I tried this again on Python 2.7.9. I could not regenerate the error you mention (TypeError: string argument without an encoding). – Mahmoud Nov 23 '15 at 09:34
  • That error only applies if you are using unicode. Try `s = u'abcdefg'`. – Two-Bit Alchemist Nov 23 '15 at 14:47
  • 7
    DO NOT DO THIS. This method ignores the entire concept of string encodings, which means it only happens to work on ASCII characters. In this day and age you cannot assume ASCII, even if you're an English speaker in an English speaking country. Python3's biggest backward incompatibility, and in my opinion most important, is fixing this whole byte = string false equivalency. Do not bring it back. – Adam Aug 25 '18 at 05:12
11

Strings are immutable in Python, which means you cannot change the existing string. But if you want to change any character in it, you could create a new string out it as follows,

def replace(s, position, character):
    return s[:position] + character + s[position+1:]

replace('King', 1, 'o')
// result: Kong

Note: If you give the position value greater than the length of the string, it will append the character at the end.

replace('Dog', 10, 's')
// result: Dogs

Manoj Kumar S
  • 634
  • 8
  • 16
10

This code is not mine. I couldn't recall the site form where, I took it. Interestingly, you can use this to replace one character or more with one or more charectors. Though this reply is very late, novices like me (anytime) might find it useful.

Change Text function.

mytext = 'Hello Zorld'
# change all Z(s) to "W"
while "Z" in mytext:
      # replace "Z" to "W"
      mytext = mytext.replace('Z', 'W')
print(mytext)
K.Vee.Shanker.
  • 299
  • 1
  • 3
  • 6
  • 13
    This doesn't answer the question. It isn't what was desired at all. – Chris Morgan Dec 16 '11 at 13:44
  • 5
    This code is bad if you want to replace *only* the first `l`. `mytext = mytext.replace('l', 'W')` -> `HeWWo Zorld` – Ooker Aug 11 '15 at 15:33
  • If you are seeking to surgically replace only 1 character (which I am) this fits the bill perfectly. Thanks! – ProfVersaggi Oct 20 '15 at 02:18
  • 2
    @ProfVersaggi That is absolutely false. See Ooker's comment above. – Two-Bit Alchemist Nov 21 '15 at 16:27
  • 6
    @Ooker If you want to replace _only_ first character you can use `mytext = mytext.replace('l', 'W',1)`. [Link to doc](https://www.tutorialspoint.com/python/string_replace.htm) – Alex Jan 30 '17 at 21:07
  • Well this might not the best answer for the given question. But it worked for me .Thank you – Kalana Mihiranga Jul 06 '21 at 08:04
  • For specific use cases, this is the fastest answer (by a little) when compared to the Method 2 in the other performance answer. 0.1318 vs 0.1391. – horta Jul 07 '21 at 14:44
9

Like other people have said, generally Python strings are supposed to be immutable.

However, if you are using CPython, the implementation at python.org, it is possible to use ctypes to modify the string structure in memory.

Here is an example where I use the technique to clear a string.

Mark data as sensitive in python

I mention this for the sake of completeness, and this should be your last resort as it is hackish.

Community
  • 1
  • 1
Unknown
  • 45,913
  • 27
  • 138
  • 182
  • 8
    Last resort? If you *ever* do this you are suddenly branded as evil! – Chris Morgan Dec 16 '11 at 13:49
  • @ChrisMorgan if your string contain a password, clearing it with s='' is not enough because the password is still written somewhere in memory. Clearing it through ctypes is the only way. – Cabu Aug 05 '16 at 13:49
  • 2
    @Cabu I would *never* under *any* circumstances accept code that did that. If your data is sensitive and you care about security like this, *`str` is not the right type for you.* Just don’t use it. Use something like `bytearray` instead. (Better still, wrap it in something that lets you treat it more or less as an opaque data so that you genuinely *can’t* retrieve a `str` from it, to protect you from accidents. There might be a library for that. No idea.) – Chris Morgan Aug 09 '16 at 01:09
6

I like f-strings:

text = f'{text[:1]}Z{text[2:]}'

In my machine this method is 10% faster than the "fast method" of using + to concatenate strings:

>>> timeit.timeit("text = 'abcdefg'; text = text[:1] + 'Z' + text[2:]", number=1000000)
1.1691178000000093
>>> timeit.timeit("text = 'abcdefg'; text = f'{text[:1]}Z{text[2:]}'", number =1000000)
0.9047831999999971
>>>
OsorioSP
  • 71
  • 1
  • 3
  • 2
    This is an interesting approach. Please consider formatting the inline code using markdown and posting details regarding your benchmarks and testing. – chb May 03 '21 at 00:24
3

Actually, with strings, you can do something like this:

oldStr = 'Hello World!'    
newStr = ''

for i in oldStr:  
    if 'a' < i < 'z':    
        newStr += chr(ord(i)-32)     
    else:      
        newStr += i
print(newStr)

'HELLO WORLD!'

Basically, I'm "adding"+"strings" together into a new string :).

angelcool.net
  • 2,505
  • 1
  • 24
  • 26
  • 5
    This is going to be very slow because every concatenation has to produce a new string object, since they are immutable, which is what this question is about. – Two-Bit Alchemist Nov 20 '15 at 21:59
0

if your world is 100% ascii/utf-8(a lot of use cases fit in that box):

b = bytearray(s, 'utf-8')
# process - e.g., lowercasing: 
#    b[0] = b[i+1] - 32
s = str(b, 'utf-8')

python 3.7.3

Paul Nathan
  • 39,638
  • 28
  • 112
  • 212
0

I would like to add another way of changing a character in a string.

>>> text = '~~~~~~~~~~~'
>>> text = text[:1] + (text[1:].replace(text[0], '+', 1))
'~+~~~~~~~~~'

How faster it is when compared to turning the string into list and replacing the ith value then joining again?.

List approach

>>> timeit.timeit("text = '~~~~~~~~~~~'; s = list(text); s[1] = '+'; ''.join(s)", number=1000000)
0.8268570480013295

My solution

>>> timeit.timeit("text = '~~~~~~~~~~~'; text=text[:1] + (text[1:].replace(text[0], '+', 1))", number=1000000)
0.588400217000526
mohammed wazeem
  • 1,310
  • 1
  • 10
  • 26
  • You're doing a combination of "Method 2" from another answer and joining it with the "replace" answer. Then you're comparing it against the .join method (which isn't performant). Overall, this doesn't seem to add anything from a performance or newness perspective over the other answers. – horta Jul 07 '21 at 14:48
0

A solution combining find and replace methods in a single line if statement could be:

```python
my_var = "stackoverflaw"
my_new_var = my_var.replace('a', 'o', 1) if my_var.find('s') != -1 else my_var
print(f"my_var = {my_var}")           # my_var = stackoverflaw
print(f"my_new_var = {my_new_var}")   # my_new_var = stackoverflow
```
eapetcho
  • 527
  • 3
  • 10
0

try this :

old_string = "mba"
string_list = list(old_string)
string_list[2] = "e"
//Replace 3rd element

new_string = "".join(string_list)

print(new_string)

0

To replace a character in a string

You can use either of the method:

Method 1

In general,

string = f'{string[:index]}{replacing_character}{string[index+1:]}'

Here

text = f'{text[:1]}Z{text[2:]}'

Method 2

In general,

string = string[:index] + replacing_character + string[index+1:]

Here,

text = text[:1] + 'Z' + text[2:]