Changer un caractère dans une chaîne en Python

385

Quelle est la manière la plus simple en Python de remplacer un caractère dans une chaîne?

Par exemple:

text = "abcdefg";
text[1] = "Z";
           ^
kostia
la source

Réponses:

535

Ne modifiez pas les chaînes.

Travaillez avec eux sous forme de listes; les transformer en chaînes uniquement lorsque cela est nécessaire.

>>> 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'

Les chaînes Python sont immuables (c'est-à-dire qu'elles ne peuvent pas être modifiées). Il y a beaucoup de raisons à cela. Utilisez des listes jusqu'à ce que vous n'ayez pas le choix, puis transformez-les en chaînes.

scvalex
la source
4
Ceux qui recherchent la vitesse / efficacité, lisez ceci
AneesAhmed777
4
"Ne modifiez pas les chaînes." pourquoi
hacksoi
2
"Créer-> modifier-> sérialiser-> affecter-> gratuit" plus efficace que s [6] = 'W'? Hmm ... Pourquoi d'autres langues le permettent, malgré ce "lot" de raisons? Intéressant de voir comment un design étrange peut être défendu (par amour, je suppose). Pourquoi ne pas suggérer d'ajouter une fonction MID (strVar, index, newChar) au noyau Python qui accède directement à la position de la mémoire char, au lieu de mélanger inutilement les octets avec la chaîne entière?
oscar
@hacksoi, @oscar, la raison est assez simple: pas besoin de recompter lors du passage de pointeurs pour implémenter la copie sur modification, ou de copier purement et simplement la chaîne entière au cas où quelqu'un voudrait modifier cette chaîne - cela conduit à une augmentation de vitesse en générique utilisation. Il n'y a pas besoin de choses comme à MIDcause des tranches:s[:index] + c + s[index+1:]
MultiSkill
1
@oscar Par langues idiotes, je veux dire qu'ils ne traitent pas avec l'unicode à moins que vous ne le leur disiez explicitement. Bien sûr, vous pouvez écrire des applications compatibles Unicode en C. Mais vous devez vous en soucier tout le temps et le tester explicitement pour éviter les problèmes. Tout est orienté machine. J'ai travaillé avec PHP avant d'apprendre Python, et ce langage est un vrai gâchis. En ce qui concerne votre note sur les processeurs rapides, je suis totalement avec vous. Mais une partie de ce problème est la désapprobation populaire de l'optimisation prématurée, qui conduit à des interprètes et des bibliothèques lents en faisant couler de nombreux cycles de processeur en cours de route.
Bachsau
202

Méthode la plus rapide?

Il y a trois façons. Pour les amateurs de vitesse, je recommande la «méthode 2»

Méthode 1

Donné par cette réponse

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

Ce qui est assez lent par rapport à la «méthode 2»

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

Méthode 2 (MÉTHODE RAPIDE)

Donné par cette réponse

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

Ce qui est beaucoup plus rapide:

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

Méthode 3:

Tableau d'octets:

timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)
1.0387420654296875
Mehdi Nellen
la source
1
Serait intéressant de voir comment il s'en sort également avec la méthode bytearray.
génial
1
Bonne suggestion. La méthode bytearray est également plus lente: timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)deux fois plus lente que la plus rapide.
Mehdi Nellen
2
Appréciez les tests, qui me font repenser la façon dont je dois manipuler les chaînes Python.
Spectral
1
Agréable. Veuillez modifier la réponse pour inclure également la méthode 3 (bytearray).
AneesAhmed777
1
Il convient de noter que la plupart du temps ici est consacré aux conversions ... (chaîne -> tableau d'octets). Si vous avez de nombreuses modifications à apporter à la chaîne, la méthode du tableau d'octets sera plus rapide.
Ian Sudbery
129
new = text[:1] + 'Z' + text[2:]
Jochen Ritzel
la source
2
Si vous ne comprenez pas pourquoi cela fonctionne, voir la réponse ci
Ooker
37

Les chaînes Python sont immuables, vous les modifiez en faisant une copie.
La façon la plus simple de faire ce que vous voulez est probablement:

text = "Z" + text[1:]

Le text[1:]retourne la chaîne textde la position 1 à la fin, les positions comptent à partir de 0, donc «1» est le deuxième caractère.

edit: Vous pouvez utiliser la même technique de découpage de chaîne pour n'importe quelle partie de la chaîne

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

Ou si la lettre n'apparaît qu'une fois que vous pouvez utiliser la technique de recherche et de remplacement suggérée ci-dessous

Martin Beckett
la source
Je mentionne le 2e personnage, IE. le personnage à la place numéro 1 (comme apposé au 1er personnage, numéro 0)
kostia
text [0] + "Z" + text [2:]
wbg
13

À partir de python 2.6 et python 3, vous pouvez utiliser des sous-tableaux qui sont mutables (peuvent être modifiés par élément contrairement aux chaînes):

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

edit: str changé en s

edit2: Comme Two-Bit Alchemist l'a mentionné dans les commentaires, ce code ne fonctionne pas avec unicode.

Mahmoud
la source
Cette réponse est incorrecte. D'une part, cela devrait l'être bytearray(s), non bytearray(str). D'autre part , cela produira: TypeError: string argument without an encoding. Si vous spécifiez un encodage, vous obtenez TypeError: an integer is required. C'est avec Python 3 ou l'unicode de Python 2. Si vous faites cela en Python 2 (avec une deuxième ligne corrigée), cela ne fonctionnera pas pour les caractères non ASCII car ils peuvent ne pas être d'un seul octet. Essayez-le avec s = 'Héllo'et vous obtiendrez 'He\xa9llo'.
Two-Bit Alchemist
J'ai essayé à nouveau sur Python 2.7.9. Je n'ai pas pu régénérer l'erreur que vous mentionnez (TypeError: argument chaîne sans encodage).
Mahmoud
Cette erreur ne s'applique que si vous utilisez unicode. Essayez s = u'abcdefg'.
Two-Bit Alchemist
4
NE FAITES PAS CELA. Cette méthode ignore le concept entier des encodages de chaînes, ce qui signifie qu'elle ne fonctionne que sur les caractères ASCII. De nos jours, vous ne pouvez pas supposer ASCII, même si vous êtes anglophone dans un pays anglophone. La plus grande incompatibilité en arrière de Python3, et à mon avis le plus important, est de corriger cette fausse équivalence byte = string. Ne le ramenez pas.
Adam
5

Comme d'autres personnes l'ont dit, les chaînes Python sont généralement censées être immuables.

Cependant, si vous utilisez CPython, l'implémentation de python.org, il est possible d'utiliser des ctypes pour modifier la structure des chaînes en mémoire.

Voici un exemple où j'utilise la technique pour effacer une chaîne.

Marquer les données comme sensibles en python

Je le mentionne par souci d'exhaustivité, et cela devrait être votre dernier recours car il est hackish.

Inconnue
la source
6
Dernier recours? Si jamais vous faites cela, vous êtes soudainement qualifié de mal!
Chris Morgan
@ChrisMorgan si votre chaîne contient un mot de passe, l'effacer avec s = '' n'est pas suffisant car le mot de passe est toujours écrit quelque part en mémoire. La suppression via des ctypes est le seul moyen.
Cabu
1
@Cabu Je ne en aucun cas accepter un code qui a fait cela. Si vos données sont sensibles et que vous vous souciez de la sécurité comme celle-ci, ce strn'est pas le bon type pour vous. Ne l'utilisez pas. Utilisez quelque chose comme à la bytearrayplace. (Mieux encore, enveloppez-le dans quelque chose qui vous permet de le traiter plus ou moins comme une donnée opaque afin que vous ne puissiez vraiment pas en récupérer un str, pour vous protéger contre les accidents. Il pourrait y avoir une bibliothèque pour cela. Aucune idée.)
Chris Morgan
4

Ce code n'est pas le mien. Je ne me souvenais pas du formulaire du site où, je l'ai pris. Fait intéressant, vous pouvez l'utiliser pour remplacer un ou plusieurs caractères par un ou plusieurs charectors. Bien que cette réponse soit très tardive, les novices comme moi (à tout moment) pourraient la trouver utile.

Changer la fonction de texte.

mytext = 'Hello Zorld'
mytext = mytext.replace('Z', 'W')
print mytext,
K.Vee.Shanker.
la source
11
Cela ne répond pas à la question. Ce n'est pas du tout ce que l'on souhaitait.
Chris Morgan
2
Ce code est mauvais si vous souhaitez remplacer uniquement le premier l. mytext = mytext.replace('l', 'W')->HeWWo Zorld
Ooker
Si vous cherchez à remplacer chirurgicalement un seul caractère (ce que je suis), cela convient parfaitement. Merci!
ProfVersaggi
@ProfVersaggi C'est absolument faux. Voir le commentaire d'Ooker ci-dessus.
Two-Bit Alchemist
3
@Ooker Si vous souhaitez remplacer uniquement le premier caractère que vous pouvez utiliser mytext = mytext.replace('l', 'W',1). Lien vers le doc
Alex
2

En fait, avec des chaînes, vous pouvez faire quelque chose comme ceci:

oldStr = 'Hello World!'    
newStr = ''

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

'HELLO WORLD!'

Fondamentalement, j'ajoute des "+" chaînes "dans une nouvelle chaîne :).

user5587487
la source
4
Cela va être très lent car chaque concaténation doit produire un nouvel objet chaîne, car ils sont immuables, c'est de cela qu'il s'agit.
Two-Bit Alchemist
0

si votre monde est à 100% ascii/utf-8(beaucoup de cas d'utilisation tiennent dans cette case):

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

Je voudrais ajouter une autre façon de changer un caractère dans une chaîne.

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

À quel point est-il plus rapide que de transformer la chaîne en liste et de remplacer la ième valeur, puis de rejoindre à nouveau?.

Approche par liste

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

Ma solution

>>> timeit.timeit("text = '~~~~~~~~~~~'; text=text[:1] + (text[1:].replace(text[0], '+', 1))", number=1000000)
0.588400217000526
mohammed wazeem
la source