Cambiar un personaje en una cadena en Python

385

¿Cuál es la forma más fácil en Python para reemplazar un carácter en una cadena?

Por ejemplo:

text = "abcdefg";
text[1] = "Z";
           ^
kostia
fuente

Respuestas:

535

No modifique las cadenas.

Trabaja con ellos como listas; conviértalos en cadenas solo cuando sea necesario.

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

Las cadenas de Python son inmutables (es decir, no se pueden modificar). Hay muchas razones para esto. Use listas hasta que no tenga otra opción, solo luego conviértalas en cadenas.

scvalex
fuente
44
Aquellos que buscan velocidad / eficiencia, lean esto
AneesAhmed777
44
"No modifique las cadenas". por qué
hacksoi
2
"Crear-> modificar-> serializar-> asignar-> libre" ¿más eficiente que s [6] = 'W'? Hmm ... ¿Por qué otros idiomas lo permiten, a pesar de ese "montón" de razones? Interesante cómo se puede defender un diseño extraño (por amor, supongo). ¿Por qué no sugerir agregar una función MID (strVar, index, newChar) al núcleo de Python que accede directamente a la posición de memoria char, en lugar de mezclar innecesariamente bytes con toda la cadena?
Oscar
@hacksoi, @oscar, la razón es bastante simple: no es necesario volver a contar cuando se pasan punteros para implementar copiar al modificar, o copiar directamente la cadena completa en caso de que alguien quiera modificar esa cadena; esto lleva a un aumento de la velocidad en genéricos utilizar. No hay necesidad de cosas como MIDdebido a los cortes:s[:index] + c + s[index+1:]
MultiSkill
1
@oscar Por idiomas tontos quiero decir que no tratan con Unicode a menos que explícitamente les digas que lo hagan. Por supuesto, puede escribir aplicaciones con capacidad Unicode en C. Pero debe preocuparse por ello todo el tiempo y debe probarlo explícitamente para evitar problemas. Todo está orientado a la máquina. Trabajé con PHP antes de aprender Python, y ese lenguaje es un desastre total. En cuanto a su nota sobre CPU rápidas, estoy totalmente con usted. Pero una parte de ese problema es la desaprobación popular de la optimización prematura, que conduce a intérpretes y bibliotecas lentas al perder muchos ciclos de CPU en el camino.
Bachsau
202

Método más rápido?

Hay tres formas Para los buscadores de velocidad recomiendo el 'Método 2'

Método 1

Dada por esta respuesta

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

Lo cual es bastante lento en comparación con el 'Método 2'

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

Método 2 (MÉTODO RÁPIDO)

Dada por esta respuesta

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

Lo cual es mucho más rápido:

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

Método 3:

Conjunto de bytes:

timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)
1.0387420654296875
Mehdi Nellen
fuente
1
Sería interesante ver cómo le va al método bytearray también.
Gaborous
1
Buena sugerencia. El método bytearray también es más lento: timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)dos veces más lento que el más rápido.
Mehdi Nellen
2
Aprecio las pruebas, que me hacen repensar cómo debo manipular las cadenas de Python.
Espectral
1
Agradable. Edite la respuesta para incluir también el método 3 (bytearray).
AneesAhmed777
1
Cabe señalar que la mayor parte del tiempo aquí se gasta en las conversiones ... (cadena -> matriz de bytes). Si tiene que realizar muchas ediciones en la cadena, el método de matriz de bytes será más rápido.
Ian Sudbery
37

Las cadenas de Python son inmutables, puede cambiarlas haciendo una copia.
La forma más fácil de hacer lo que quieres es probablemente:

text = "Z" + text[1:]

El text[1:]devuelve la cadena textdesde la posición 1 hasta el final, las posiciones cuentan desde 0, por lo que '1' es el segundo carácter.

editar: puede usar la misma técnica de corte de cadena para cualquier parte de la cadena

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

O si la letra solo aparece una vez, puede usar la técnica de búsqueda y reemplazo que se sugiere a continuación

Martin Beckett
fuente
Mencioné el segundo personaje, IE. el carácter en el lugar número 1 (serie respecto a la primera de carácter, número 0)
Kostia
texto [0] + "Z" + texto [2:]
wbg
13

Comenzando con python 2.6 y python 3, puede usar bytearrays que son mutables (se pueden cambiar por elementos a diferencia de las cadenas):

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

editar: Cambiado str a s

edit2: Como mencionó el alquimista de dos bits en los comentarios, este código no funciona con unicode.

Mahmoud
fuente
Esta respuesta es incorrecta. Por un lado, debería ser bytearray(s), no bytearray(str). Por otra parte, esto producirá: TypeError: string argument without an encoding. Si especificas una codificación, entonces obtienes TypeError: an integer is required. Eso es con Python 3 o Python 2's unicode. Si hace esto en Python 2 (con una segunda línea corregida), no funcionará para los caracteres que no son ASCII porque pueden no ser solo un byte. Pruébalo s = 'Héllo'y obtendrás 'He\xa9llo'.
Alquimista de dos bits el
Intenté esto nuevamente en Python 2.7.9. No pude volver a generar el error que menciona (TypeError: argumento de cadena sin codificación).
Mahmoud
Ese error solo se aplica si está utilizando Unicode. Tratar s = u'abcdefg'.
Two-Bit Alchemist
44
NO HAGAS ESTO. Este método ignora todo el concepto de codificación de cadenas, lo que significa que solo funciona en caracteres ASCII. En este día y edad no puede asumir ASCII, incluso si habla inglés en un país de habla inglesa. La mayor incompatibilidad hacia atrás de Python3, y en mi opinión la más importante, es arreglar todo este byte = string false equivalencia. No lo traigas de vuelta.
Adam
5

Como han dicho otras personas, generalmente se supone que las cadenas de Python son inmutables.

Sin embargo, si está utilizando CPython, la implementación en python.org, es posible usar ctypes para modificar la estructura de la cadena en la memoria.

Aquí hay un ejemplo donde uso la técnica para borrar una cadena.

Marcar datos como sensibles en python

Menciono esto en aras de la integridad, y este debería ser su último recurso, ya que es hackish.

Desconocido
fuente
66
¿Último recurso? ¡Si alguna vez haces esto, de repente eres calificado de malvado!
Chris Morgan
@ChrisMorgan si su cadena contiene una contraseña, borrarla con s = '' no es suficiente porque la contraseña todavía está escrita en algún lugar de la memoria. Limpiarlo a través de ctypes es la única forma.
Cabu
1
@Cabu Nunca, bajo ninguna circunstancia, aceptaría código que hiciera eso. Si sus datos son confidenciales y le preocupa la seguridad como esta, strno es el tipo adecuado para usted. Simplemente no lo uses. Use algo como en su bytearraylugar. (Mejor aún, envuélvalo en algo que le permita tratarlo más o menos como datos opacos para que realmente no pueda recuperar uno strde él, para protegerlo de accidentes. Puede haber una biblioteca para eso. Ni idea).
Chris Morgan
4

Este código no es mío. No podía recordar el sitio donde lo tomé. Curiosamente, puede usar esto para reemplazar un personaje o más con uno o más personajes. Aunque esta respuesta es muy tardía, los principiantes como yo (en cualquier momento) pueden encontrarla útil.

Cambiar la función de texto.

mytext = 'Hello Zorld'
mytext = mytext.replace('Z', 'W')
print mytext,
K.Vee.Shanker.
fuente
11
Esto no responde la pregunta. No es lo que se deseaba en absoluto.
Chris Morgan
2
Este código es malo si desea reemplazar solo el primero l. mytext = mytext.replace('l', 'W')->HeWWo Zorld
Ooker
Si está buscando reemplazar quirúrgicamente solo 1 carácter (que yo soy), esto encaja perfectamente. ¡Gracias!
ProfVersaggi
@ProfVersaggi Eso es absolutamente falso. Ver el comentario de Ooker arriba.
Alquimista de dos bits el
3
@Ooker Si quieres reemplazar solo el primer personaje que puedes usar mytext = mytext.replace('l', 'W',1). Enlace al documento
Alex
2

En realidad, con cadenas, puedes hacer algo como esto:

oldStr = 'Hello World!'    
newStr = ''

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

'HELLO WORLD!'

Básicamente, estoy "agregando" + "cadenas" juntas en una nueva cadena :).

usuario5587487
fuente
44
Esto va a ser muy lento porque cada concatenación debe producir un nuevo objeto de cadena, ya que son inmutables, de eso se trata esta pregunta.
Alquimista de dos bits el
0

si su mundo es 100% ascii/utf-8(muchos casos de uso caben en ese cuadro):

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
fuente
0

Me gustaría agregar otra forma de cambiar un carácter en una cadena.

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

¿Qué tan rápido es en comparación con convertir la cadena en una lista y reemplazar el i-ésimo y luego unir nuevamente?

Enfoque de lista

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

Mi solución

>>> timeit.timeit("text = '~~~~~~~~~~~'; text=text[:1] + (text[1:].replace(text[0], '+', 1))", number=1000000)
0.588400217000526
Mohammed Wazeem
fuente