Alterando um caractere em uma string em Python

385

Qual é a maneira mais fácil no Python de substituir um caractere em uma string?

Por exemplo:

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

Respostas:

534

Não modifique strings.

Trabalhe com eles como listas; transformá-los em strings somente quando necessário.

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

Strings Python são imutáveis ​​(ou seja, não podem ser modificadas). Existem muitas razões para isso. Use as listas até não ter escolha, e depois transforme-as em strings.

scvalex
fonte
4
Aqueles que procuram velocidade / eficiência, leia isto
AneesAhmed777:
4
"Não modifique strings." por que
hacksoi 10/10
2
"Criar-> modificar-> serializar-> atribuir-> livre" mais eficaz que s [6] = 'W'? Hmm ... Por que outras línguas o permitem, apesar desse "monte" de razões? Interessante como um design estranho pode ser defendido (por amor, suponho). Por que não sugerir a adição de uma função MID (strVar, index, newChar) ao núcleo do Python que acessa diretamente a posição da memória do char, em vez de bytes desnecessários embaralhando a string inteira?
oscar
@hacksoi, @oscar, o motivo é bastante simples: não há necessidade de recontar ao passar ponteiros para implementar a cópia por modificação, ou copiar completamente toda a string no caso de alguém querer modificar essa string - isso leva ao aumento da velocidade genérica usar. Não há necessidade de coisas como MIDdevido a fatias:s[:index] + c + s[index+1:]
MultiSkill 4/18/18
11
@oscar Por linguagens idiotas, quero dizer que eles não lidam com unicode, a menos que você diga explicitamente. É claro que você pode escrever aplicativos compatíveis com unicode em C. Mas você precisa se preocupar com isso o tempo todo e precisa testá-lo explicitamente para evitar problemas. Tudo é orientado à máquina. Eu trabalhei com PHP antes de aprender Python, e essa linguagem é uma bagunça total. Em relação à sua nota em CPUs rápidas, estou totalmente com você. Mas parte desse problema é a desaprovação popular da otimização prematura, o que leva a lentidão de intérpretes e bibliotecas, vazando muitos ciclos de CPU no caminho.
Bachsau 07/07/19
202

Método mais rápido?

Existem três maneiras. Para os que procuram velocidade, eu recomendo o "Método 2"

Método 1

Dado por esta resposta

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

O que é bem lento em comparação com o "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)

Dado por esta resposta

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

O que é muito mais rápido:

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

Método 3:

Matriz de bytes:

timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)
1.0387420654296875
Mehdi Nellen
fonte
11
Seria interessante ver como ele se compara ao método bytearray.
precisa saber é
11
Boa sugestão. O método bytearray também é mais lento: timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)duas vezes mais lento que o mais rápido.
Mehdi Nellen
2
Aprecie os testes, o que me faz repensar como devo manipular seqüências de caracteres Python.
Spectral
11
Agradável. Edite a resposta para incluir também o método 3 (bytearray).
precisa saber é o seguinte
11
Deve-se observar que a maior parte do tempo aqui é gasta nas conversões ... (string -> matriz de bytes). Se você tiver muitas edições para fazer na string, o método da matriz de bytes será mais rápido.
Ian Sudbery
37

Seqüências de caracteres Python são imutáveis, você as altera fazendo uma cópia.
A maneira mais fácil de fazer o que você quer é provavelmente:

text = "Z" + text[1:]

Os text[1:]retornos a corda no textda posição 1 para o final, as posições contar de 0 so '1' é o segundo personagem.

editar: você pode usar a mesma técnica de corte de cadeia para qualquer parte da cadeia

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

Ou se a letra aparecer apenas quando você puder usar a técnica de pesquisa e substituição sugerida abaixo

Martin Beckett
fonte
Eu menti o segundo personagem, IE. o personagem no lugar de número 1 (como apposed para o 1º carácter, número 0)
Kostia
texto [0] + "Z" + texto [2:]
wbg
13

Começando com o python 2.6 e python 3, você pode usar bytearrays que são mutáveis ​​(podem ser alterados em termos de elementos, diferentemente das strings):

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

edit: Alterado str para s

edit2: Como o Alquimista de dois bits mencionado nos comentários, esse código não funciona com unicode.

Mahmoud
fonte
Esta resposta está incorreta. Por um lado, deveria ser bytearray(s), não bytearray(str). Por outro lado, este irá produzir: TypeError: string argument without an encoding. Se você especificar uma codificação, receberá TypeError: an integer is required. Isso ocorre com o unicode do Python 3 ou do Python 2. Se você fizer isso no Python 2 (com uma segunda linha corrigida), ele não funcionará para caracteres não ASCII, pois eles podem não ter apenas um byte. Experimente s = 'Héllo'e você receberá 'He\xa9llo'.
Two-Bit Alchemist
Eu tentei isso novamente no Python 2.7.9. Não foi possível gerar novamente o erro mencionado (TypeError: argumento de string sem uma codificação).
Mahmoud
Esse erro se aplica apenas se você estiver usando unicode. Tente s = u'abcdefg'.
Two-Bit Alchemist
4
NÃO FAÇA ISSO. Esse método ignora todo o conceito de codificações de seqüência de caracteres, o que significa que só funciona em caracteres ASCII. Hoje em dia, você não pode assumir o ASCII, mesmo se você fala inglês em um país de língua inglesa. A maior incompatibilidade com versões anteriores do Python3 e, na minha opinião, mais importante, está corrigindo toda a byte = string equivalência falsa. Não traga de volta.
Adam
5

Como outras pessoas disseram, geralmente as seqüências de caracteres Python são imutáveis.

No entanto, se você estiver usando o CPython, a implementação em python.org, é possível usar ctypes para modificar a estrutura de strings na memória.

Aqui está um exemplo em que eu uso a técnica para limpar uma string.

Marcar dados como sensíveis em python

Menciono isso por uma questão de perfeição, e esse deve ser seu último recurso, pois é um hack.

Desconhecido
fonte
6
Último recurso? Se você nunca fazer isso de repente você está marcado como mal!
Chris Morgan
@ChrisMorgan se a sua string contiver uma senha, limpá-la com s = '' não será suficiente porque a senha ainda está gravada em algum lugar da memória. Limpá-lo através de ctypes é o único caminho.
Cabu
11
@Cabu Eu nunca aceitaria, sob nenhuma circunstância, código que fizesse isso. Se seus dados são confidenciais e você se preocupa com segurança como essa, strnão é o tipo certo para você. Só não use. Use algo como em bytearrayvez disso. (Melhor ainda, envolva-o em algo que permita tratá-lo mais ou menos como dados opacos, para que você realmente não possa recuperar um deles str, para protegê-lo de acidentes. Pode haver uma biblioteca para isso. Não faço ideia.)
9788 Chris Morgan
4

Este código não é meu. Não conseguia me lembrar do formulário do site onde o peguei. Curiosamente, você pode usar isso para substituir um caractere ou mais por um ou mais caracteres. Embora essa resposta seja muito tardia, novatos como eu (a qualquer momento) podem achar útil.

Alterar função de texto.

mytext = 'Hello Zorld'
mytext = mytext.replace('Z', 'W')
print mytext,
K.Vee.Shanker.
fonte
11
Isso não responde à pergunta. Não é o que era desejado.
Chris Morgan
2
Esse código é ruim se você deseja substituir apenas o primeiro l. mytext = mytext.replace('l', 'W')->HeWWo Zorld
Ooker
Se você deseja substituir cirurgicamente apenas 1 caractere (o que eu sou), isso se encaixa perfeitamente. Obrigado!
ProfVersaggi
@ProfVersaggi Isso é absolutamente falso. Veja o comentário de Ooker acima.
Alquimista de dois bits
3
@Ooker Se você deseja substituir apenas o primeiro caractere, pode usar mytext = mytext.replace('l', 'W',1). Link para doc
Alex
2

Na verdade, com strings, você pode fazer algo assim:

oldStr = 'Hello World!'    
newStr = ''

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

'HELLO WORLD!'

Basicamente, estou "adicionando" + "strings" em uma nova string :).

user5587487
fonte
4
Isso será muito lento, porque toda concatenação deve produzir um novo objeto de string, pois eles são imutáveis, e é disso que se trata esta pergunta.
Two-Bit Alchemist
0

se seu mundo for 100% ascii/utf-8(muitos casos de uso se encaixam nessa caixa):

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

Gostaria de adicionar outra maneira de alterar um caractere em uma string.

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

Quão mais rápido é quando comparado a transformar a string em lista e substituir o i-ésimo valor e depois juntar-se novamente?

Abordagem de lista

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

Minha solução

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