Ändern eines Zeichens in einer Zeichenfolge in Python

385

Was ist in Python der einfachste Weg, ein Zeichen in einer Zeichenfolge zu ersetzen?

Zum Beispiel:

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

Antworten:

534

Ändern Sie keine Zeichenfolgen.

Arbeiten Sie mit ihnen als Listen; Verwandeln Sie sie nur bei Bedarf in Zeichenfolgen.

>>> 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 sind unveränderlich (dh sie können nicht geändert werden). Dafür gibt es viele Gründe. Verwenden Sie Listen, bis Sie keine andere Wahl haben, und verwandeln Sie sie erst dann in Zeichenfolgen.

scvalex
quelle
4
Diejenigen, die Geschwindigkeit / Effizienz suchen, lesen dies
AneesAhmed777
4
"Ändern Sie keine Zeichenfolgen." warum
Hacksoi
2
"Erstellen-> Ändern-> Serialisieren-> Zuweisen-> Frei" effizienter als s [6] = 'W'? Hmm ... Warum erlauben es andere Sprachen trotz dieser "vielen" Gründe? Interessant, wie ein seltsames Design verteidigt werden kann (aus Liebe, nehme ich an). Warum nicht vorschlagen, dem Python-Kern eine Funktions-MID (strVar, index, newChar) hinzuzufügen, die direkt auf die Zeichen-Speicherposition zugreift, anstatt unnötig Bytes mit der gesamten Zeichenfolge zu mischen?
Oscar
@hacksoi, @oscar, der Grund ist ganz einfach: Sie müssen nicht erneut zählen, wenn Sie Zeiger weitergeben, um Copy-on-Modify zu implementieren, oder die gesamte Zeichenfolge direkt kopieren, falls jemand diese Zeichenfolge ändern möchte - dies führt zu einer Geschwindigkeitssteigerung bei generischen Zeichenfolgen verwenden. Es gibt keine Notwendigkeit für Dinge wie MIDwegen Scheiben:s[:index] + c + s[index+1:]
MultiSkill
1
@oscar Mit dummen Sprachen meine ich, dass sie sich nicht mit Unicode befassen, es sei denn, Sie sagen es ihnen ausdrücklich. Natürlich können Sie Unicode-fähige Anwendungen in C schreiben. Sie müssen sich jedoch ständig darum kümmern und diese explizit testen, um Probleme zu vermeiden. Alles ist maschinenorientiert. Ich habe mit PHP gearbeitet, bevor ich Python gelernt habe, und diese Sprache ist ein totales Chaos. In Bezug auf Ihre Anmerkung zu schnellen CPUs bin ich total bei Ihnen. Ein Teil dieses Problems ist jedoch die weit verbreitete Ablehnung einer vorzeitigen Optimierung, die zu langsamen Interpreten und Bibliotheken führt, indem viele CPU-Zyklen auf dem Weg verloren gehen.
Bachsau
202

Schnellste Methode?

Es gibt drei Möglichkeiten. Für die Geschwindigkeitssucher empfehle ich 'Methode 2'

Methode 1

Gegeben durch diese Antwort

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

Was im Vergleich zu 'Methode 2' ziemlich langsam ist.

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

Methode 2 (SCHNELLE METHODE)

Gegeben durch diese Antwort

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

Welches ist viel schneller:

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

Methode 3:

Byte-Array:

timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)
1.0387420654296875
Mehdi Nellen
quelle
1
Es wäre interessant zu sehen, wie es auch gegen die Bytearray-Methode abschneidet.
gaborous
1
Guter Vorschlag. Die Bytearray-Methode ist ebenfalls langsamer: timeit.timeit("text = 'abcdefg'; s = bytearray(text); s[1] = 'Z'; str(s)", number=1000000)doppelt so langsam wie die schnellste.
Mehdi Nellen
2
Schätzen Sie die Tests, die mich überdenken lassen, wie ich Python-Strings manipulieren soll.
Spectral
1
Nett. Bitte bearbeiten Sie die Antwort, um auch Methode 3 einzuschließen (bytearray).
AneesAhmed777
1
Es ist zu beachten, dass die meiste Zeit hier für die Konvertierungen aufgewendet wird ... (Zeichenfolge -> Byte-Array). Wenn Sie viele Änderungen an der Zeichenfolge vornehmen müssen, ist die Byte-Array-Methode schneller.
Ian Sudbery
129
new = text[:1] + 'Z' + text[2:]
Jochen Ritzel
quelle
2
Wenn Sie nicht verstehen, warum dies funktioniert,
lesen
37

Python-Strings sind unveränderlich. Sie ändern sie, indem Sie eine Kopie erstellen.
Der einfachste Weg, um das zu tun, was Sie wollen, ist wahrscheinlich:

text = "Z" + text[1:]

Das text[1:]gibt die Zeichenfolge textvon Position 1 bis zum Ende zurück. Die Positionen zählen von 0, sodass '1' das zweite Zeichen ist.

Bearbeiten: Sie können für jeden Teil der Zeichenfolge dieselbe Technik zum Schneiden von Zeichenfolgen verwenden

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

Wenn der Buchstabe nur einmal angezeigt wird, können Sie die unten vorgeschlagene Such- und Ersetzungstechnik verwenden

Martin Beckett
quelle
Ich erwähne das 2. Zeichen, IE. das Zeichen an Ort Nummer 1 (wie an das 1. Zeichen, Nummer 0
angehängt
text [0] + "Z" + text [2:]
wbg
13

Ab Python 2.6 und Python 3 können Sie veränderbare Bytearrays verwenden (die im Gegensatz zu Strings elementweise geändert werden können):

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

edit: str wurde in s geändert

edit2: Wie der Zwei-Bit-Alchemist in den Kommentaren erwähnt hat, funktioniert dieser Code nicht mit Unicode.

Mahmoud
quelle
Diese Antwort ist falsch. Zum einen sollte es bytearray(s)nicht sein bytearray(str). Zum anderen wird dies ergeben : TypeError: string argument without an encoding. Wenn Sie eine Codierung angeben, erhalten Sie TypeError: an integer is required. Das ist mit Python 3 oder Python 2 Unicode. Wenn Sie dies in Python 2 (mit einer korrigierten zweiten Zeile) tun, funktioniert dies nicht für Nicht-ASCII-Zeichen, da diese möglicherweise nicht nur ein Byte lang sind. Probieren Sie es aus s = 'Héllo'und Sie werden bekommen 'He\xa9llo'.
Zwei-Bit-Alchemist
Ich habe es unter Python 2.7.9 erneut versucht. Ich konnte den von Ihnen erwähnten Fehler nicht neu generieren (TypeError: Zeichenfolgenargument ohne Codierung).
Mahmoud
Dieser Fehler tritt nur auf, wenn Sie Unicode verwenden. Versuchen Sie es s = u'abcdefg'.
Zwei-Bit-Alchemist
4
MACH DAS NICHT. Diese Methode ignoriert das gesamte Konzept der Zeichenfolgencodierung, was bedeutet, dass nur ASCII-Zeichen verarbeitet werden. Heutzutage können Sie ASCII nicht mehr annehmen, selbst wenn Sie Englisch in einem englischsprachigen Land sprechen. Die größte Abwärtsinkompatibilität von Python3, und meiner Meinung nach am wichtigsten, besteht darin, diese ganze Byte = String-Falschäquivalenz zu beheben. Bring es nicht zurück.
Adam
5

Wie andere Leute gesagt haben, sollten Python-Strings im Allgemeinen unveränderlich sein.

Wenn Sie jedoch CPython verwenden, die Implementierung auf python.org, können Sie mit ctypes die Zeichenfolgenstruktur im Speicher ändern.

Hier ist ein Beispiel, in dem ich die Technik verwende, um eine Zeichenfolge zu löschen.

Markieren Sie Daten in Python als vertraulich

Ich erwähne dies der Vollständigkeit halber, und dies sollte Ihr letzter Ausweg sein, da es hackisch ist.

Unbekannt
quelle
6
Letzter Ausweg? Wenn Sie dies jemals tun, werden Sie plötzlich als böse gebrandmarkt!
Chris Morgan
@ChrisMorgan Wenn Ihre Zeichenfolge ein Kennwort enthält, reicht es nicht aus, es mit s = '' zu löschen, da das Kennwort noch irgendwo im Speicher geschrieben ist. Das Löschen durch ctypes ist der einzige Weg.
Cabu
1
@Cabu Ich würde nie unter irgendwelchen Umständen akzeptieren Code, der das tat. Wenn Ihre Daten vertraulich sind und Sie sich um diese Sicherheit kümmern, strist dies nicht der richtige Typ für Sie. Benutze es einfach nicht. Verwenden Sie bytearraystattdessen so etwas wie . (Besser noch, wickeln Sie es in etwas ein, mit dem Sie es mehr oder weniger als undurchsichtige Daten behandeln können, sodass Sie wirklich keine Daten abrufen strkönnen, um sich vor Unfällen zu schützen. Möglicherweise gibt es dafür eine Bibliothek. Keine Ahnung.)
Chris Morgan
4

Dieser Code gehört nicht mir. Ich konnte mich nicht an das Site-Formular erinnern, wo ich es genommen habe. Interessanterweise können Sie damit ein oder mehrere Zeichen durch ein oder mehrere Zeichen ersetzen. Obwohl diese Antwort sehr spät ist, könnten Anfänger wie ich (jederzeit) sie nützlich finden.

Textfunktion ändern.

mytext = 'Hello Zorld'
mytext = mytext.replace('Z', 'W')
print mytext,
K.Vee.Shanker.
quelle
11
Dies beantwortet die Frage nicht. Es ist überhaupt nicht das, was gewünscht wurde.
Chris Morgan
2
Dieser Code ist schlecht, wenn Sie nur den ersten ersetzen möchten l. mytext = mytext.replace('l', 'W')->HeWWo Zorld
Ooker
Wenn Sie nur 1 Zeichen (was ich bin) chirurgisch ersetzen möchten, passt dies perfekt zur Rechnung. Vielen Dank!
ProfVersaggi
@ProfVersaggi Das ist absolut falsch. Siehe Ookers Kommentar oben.
Zwei-Bit-Alchemist
3
@Ooker Wenn Sie nur das erste Zeichen ersetzen möchten, können Sie verwenden mytext = mytext.replace('l', 'W',1). Link zu doc
Alex
2

Mit Strings können Sie tatsächlich Folgendes tun:

oldStr = 'Hello World!'    
newStr = ''

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

'HELLO WORLD!'

Grundsätzlich "addiere" ich + "Strings" zu einem neuen String :).

user5587487
quelle
4
Dies wird sehr langsam sein, da jede Verkettung ein neues Zeichenfolgenobjekt erzeugen muss, da sie unveränderlich sind, worum es bei dieser Frage geht.
Zwei-Bit-Alchemist
0

Wenn Ihre Welt zu 100% ist ascii/utf-8(viele Anwendungsfälle passen in diese 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
quelle
0

Ich möchte eine andere Möglichkeit hinzufügen, ein Zeichen in einer Zeichenfolge zu ändern.

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

Wie schnell ist es im Vergleich dazu, die Zeichenfolge in eine Liste umzuwandeln und den i-ten Wert zu ersetzen und dann wieder zu verbinden?

Listenansatz

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

Meine Lösung

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