13.4 Strings – str, bytes, bytearray
Dieser Abschnitt behandelt Pythons Umgang mit Zeichenketten und insbesondere die Eigenschaften der dafür bereitgestellten Datentypen str, bytes und bytearray.
Wie Sie bereits gelernt haben, handelt es sich bei Strings um Folgen von Zeichen. Dies bedeutet, dass alle Operationen für sequenzielle Typen für sie verfügbar sind.[ 45 ](Da bytearray im Unterschied zu str und bytes ein mutabler Datentyp ist, werden zusätzlich zum Grundstock der Operationen für sequenzielle Datentypen neben den in diesem Kapitel vorgestellten auch noch viele der Methoden des list-Datentyps unterstützt. Eine Auflistung der Methoden, die bytearray als mutabler sequenzieller Datentyp bietet, finden Sie in Abschnitt 21.7.3, »Abschnitt 21.7.3«. )
Die Zeichen, die eine Instanz des Datentyps str speichern kann, sind Buchstaben, Satz- und Leerzeichen oder auch Umlaute. Im Gegensatz dazu sind die Datentypen bytes und bytearray für die Speicherung von Binärdaten vorgesehen. Daher bestehen Instanzen der Datentypen bytes und bytearray aus einer Folge einzelner Bytes, also ganzen Zahlen von 0 bis 255.
Wir werden uns bis auf Weiteres nur mit str-Instanzen beschäftigen, da sich der Umgang mit str nicht wesentlich von dem mit bytes unterscheidet. Einzig beim Umwandeln von str nach bytes und umgekehrt gibt es einige Stolpersteine, die in Abschnitt 13.4.4 über Zeichensätze und Sonderzeichen besprochen werden.
Um neue str-Instanzen zu erzeugen, gibt es folgende Literale:
>>> string1 = "Ich wurde mit doppelten Hochkommata definiert"
>>> string2 = 'Ich wurde mit einfachen Hochkommata definiert'
Der gewünschte Inhalt des Strings wird zwischen die Hochkommata geschrieben, darf allerdings keine Zeilenvorschübe enthalten (im folgenden Beispiel wurde am Ende der ersten Zeile (¢) gedrückt):
>>> s = "Erste Zeile
File "<stdin>", line 1
s = "Erste Zeile
^
SyntaxError: EOL while scanning string literal
String-Konstanten, die sich auch über mehrere Zeilen erstrecken können, werden durch """ bzw. ''' eingefasst:[ 46 ](Sie werden in Python-Code häufig auf Strings stoßen, die Klassen, Funktionen oder Module beschreiben. Für diese Docstrings wird in der Regel das Literal mit drei Anführungszeichen verwendet. Weitere Informationen zu Docstrings finden Sie in Abschnitt 36.1. )
>>> string3 = """Erste Zeile!
... Ui, noch eine Zeile"""
Stehen zwei String-Literale unmittelbar oder durch Leerzeichen getrennt hintereinander, werden sie von Python zu einem String verbunden:
>>> string = "Erster Teil" "Zweiter Teil"
>>> string
'Erster TeilZweiter Teil'
Wie Sie im Beispiel sehen, sind die Leerzeichen zwischen den Literalen bei der Verkettung nicht mehr vorhanden.
Diese Art der Verkettung eignet sich sehr gut, um lange oder unübersichtliche Strings auf mehrere Programmzeilen aufzuteilen, ohne dass die Zeilenvorschübe und Leerzeichen im Resultat gespeichert werden, wie es bei Strings mit """ oder ''' der Fall wäre. Um diese Aufteilung zu erreichen, schreibt man die String-Teile in runde Klammern:
>>> a = ("Stellen Sie sich einen schrecklich "
... "komplizierten String vor, den man "
... "auf keinen Fall in eine Zeile schreiben "
... "kann.")
>>> a
'Stellen Sie sich einen schrecklich komplizierten String vor, den man auf keinen Fall in eine Zeile schreiben kann.'
Wie Sie sehen, wurde der String so gespeichert, als ob er in einer einzigen Zeile definiert worden wäre.
Die Erzeugung von bytes-Instanzen funktioniert genauso wie die oben beschriebene Erzeugung von str-Instanzen. Der einzige Unterschied ist, dass Sie dem String-Literal ein kleines b voranstellen müssen, um einen bytes-String zu erhalten:
>>> string1 = b"Ich bin bytes!"
>>> string1
b'Ich bin bytes!'
>>> type(string1)
<class 'bytes'>
Die anderen Arten der String-Erzeugung funktionieren für bytes analog.
Um eine neue Instanz des Typs bytearray zu erzeugen, dient die gleichnamige Built-in Function bytearray.
>>> string1 = b"Hallo Welt"
>>> string2 = bytearray(string1)
>>> string2
bytearray(b'Hallo Welt')
Auf diese Weise lässt sich also eine bytearray-Instanz erzeugen, die ihren Wert von einer bestehenden bytes-Instanz übernimmt.
Übergibt man der Built-in Function bytearray eine ganze Zahl k als Parameter, wird ein neues bytearray der Länge k erzeugt, wobei jedes der Bytes den Wert Null zugewiesen bekommt:
>>> bytearray(7)
bytearray(b'\x00\x00\x00\x00\x00\x00\x00')
Was es mit der Darstellung »\x00« dieser Zeichen auf sich hat, erfahren Sie im nächsten Abschnitt sowie in Abschnitt 13.4.4 im Kontext der String-Codierung.
13.4.1 Steuerzeichen
Es gibt besondere Textelemente, die den Textfluss steuern und sich auf dem Bildschirm nicht als einzelne Zeichen darstellen lassen. Zu diesen sogenannten Steuerzeichen zählen unter anderem der Zeilenvorschub, der Tabulator oder der Rückschritt (von engl. backspace). Die Darstellung solcher Zeichen innerhalb von String-Literalen erfolgt mittels spezieller Zeichenfolgen, den Escape-Sequenzen. Escape-Sequenzen werden von einem Backslash \ eingeleitet, dem die Kennung des gewünschten Sonderzeichens folgt. Die Escape-Sequenz "\n" steht beispielsweise für einen Zeilenumbruch:
>>> a = "Erste Zeile\nZweite Zeile"
>>> a
'Erste Zeile\nZweite Zeile'
>>> print(a)
Erste Zeile
Zweite Zeile
Beachten Sie bitte den Unterschied zwischen der Ausgabe mit print und der ohne print im interaktiven Modus: Die print-Anweisung setzt die Steuerzeichen in ihre Bildschirmdarstellung um (bei "\n" wird zum Beispiel eine neue Zeile begonnen), während die Ausgabe ohne print ein String-Literal mit den Escape-Sequenzen der Sonderzeichen auf dem Bildschirm anzeigt.
Für Steuerzeichen gibt es in Python die folgenden Escape-Sequenzen:
Escape-Sequenz | Bedeutung |
---|---|
\a | Bell (BEL) erzeugt einen Signalton. |
\b | Backspace (BS) setzt die Ausgabeposition um ein Zeichen zurück. |
\f | Formfeed (FF) erzeugt einen Seitenvorschub. |
\n | Linefeed (LF) setzt die Ausgabeposition in die nächste Zeile. |
\r | Carriage Return (CR) setzt die Ausgabeposition an den Anfang der nächsten Zeile. |
\t | Horizontal Tab (TAB) hat die gleiche Bedeutung wie die Tabulatortaste. |
\v | Vertikaler Tabulator (VT); dient zur vertikalen Einrückung. |
\" | doppeltes Hochkomma |
\' | einfaches Hochkomma |
\\ | Backslash, der wirklich als solcher in dem String erscheinen soll |
Steuerzeichen stammen aus der Zeit, als die Ausgaben hauptsächlich über Drucker erfolgten. Deshalb haben einige dieser Zeichen heute nur noch eine geringe praktische Bedeutung.
Die Escape-Sequenzen für einfache und doppelte Hochkommata sind notwendig, weil Python diese Zeichen als Begrenzung für String-Literale verwendet. Soll die Art von Hochkomma, die für die Begrenzung eines Strings verwendet wurde, innerhalb dieses Strings als Zeichen vorkommen, muss dort das entsprechende Hochkomma als Escape-Sequenz angegeben werden:
>>> a = "Das folgende Hochkomma muss nicht codiert werden ' "
>>> b = "Dieses doppelte Hochkomma schon \" "
>>> c = 'Das gilt auch in Strings mit einfachen Hochkommata " '
>>> d = 'Hier muss eine Escape-Sequenz benutzt werden \' '
In Abschnitt 13.4.4, »Zeichensätze und Sonderzeichen«, werden wir auf Escape-Sequenzen zurückkommen, um damit beliebige Sonderzeichen wie beispielsweise Umlaute oder das Eurozeichen zu codieren.
Das automatische Ersetzen von Escape-Sequenzen ist manchmal lästig, insbesondere dann, wenn sehr viele Backslashs in einem String vorkommen sollen. Für diesen Zweck gibt es in Python die Präfixe r oder R, die einem String-Literal vorangestellt werden können. Diese Präfixe markieren das Literal als einen sogenannten Raw-String (dt. »roh«), was dazu führt, dass alle Backslashs eins zu eins in den Resultat-String übernommen werden:
>>> "Ein \tString mit \\ vielen \nEscape-Sequenzen\t"
'Ein \tString mit \\ vielen \nEscape-Sequenzen\t'
>>> r"Ein \tString mit \\ vielen \nEscape-Sequenzen\t"
'Ein \\tString mit \\\\ vielen \\nEscape-Sequenzen\\t'
>>> print(r"Ein \tString mit \\ vielen \nEscape-Sequenzen\t")
Ein \tString mit \\ vielen \nEscape-Sequenzen\t
Wie Sie an den doppelten Backslashs im Literal des Resultats und der Ausgabe mit print sehen können, wurden die Escape-Sequenzen nicht als maskierte Sonderzeichen interpretiert.
Durch das Präfix rb bzw. br können Sie Raw-Strings vom Typ bytes erzeugen.
[»] Hinweis
Mit Python 3.3 wurde das u-Literal wieder eingeführt, um die Migration von Python 2.x auf Python 3 zu erleichtern. Seitdem können str-Instanzen wahlweise mit einem oder ohne ein vorangestelltes u erzeugt werden:
>>> u"Der Habicht ist der Vogel des Jahres 2015"
'Der Habicht ist der Vogel des Jahres 2015'
Wenn wir im Folgenden von Whitespaces sprechen, sind alle Zeichen gemeint, die beim Drucken keine Tinte benötigen, z. B. Leerzeichen oder Zeilenvorschübe. Es handelt sich um die Zeichen in Tabelle 13.9:
String-Literal | Name |
---|---|
" " | Leerzeichen |
"\n" | Zeilenvorschub |
"\v" | vertikaler Tabulator |
"\t" | horizontaler Tabulator |
"\f" | Formfeed |
"\r" | Carriage Return |
13.4.2 String-Methoden
String-Instanzen verfügen zusätzlich zu den Methoden für sequenzielle Datentypen über weitere Methoden, die den Umgang mit Zeichenketten vereinfachen. In den folgenden Abschnitten besprechen wir jeweils thematisch zusammengehörige Methoden in den Kategorien
- Trennen von Strings,
- Suchen von Teil-Strings,
- Ersetzen von Teil-Strings,
- Entfernen bestimmter Zeichen am Anfang oder am Ende eines Strings,
- Ausrichten von Strings,
- String-Tests,
- Verkettung von Elementen in sequenziellen Datentypen,
- String-Formatierung.
Trennen von Strings
Um Strings nach bestimmten Regeln in mehrere Teile zu zerlegen, stehen folgende Methoden zur Verfügung:
Methode | Beschreibung |
---|---|
s.split([sep, maxsplit]) | Teilt s bei Vorkommen von sep. Die Suche beginnt am String-Anfang. |
s.rsplit([sep, maxsplit]) | Teilt s bei Vorkommen von sep. Die Suche beginnt am String-Ende. |
s.splitlines([keepends]) | Teilt s bei Vorkommen von Zeilenvorschüben. |
s.partition(sep) |
Teilt s bei Vorkommen von sep. Die Suche beginnt am Anfang des Strings. Die resultierende Liste enthält neben den Spaltprodukten auch das Trennzeichen sep. |
s.rpartition(sep) |
Teilt s bei Vorkommen von sep. Die Suche beginnt am String-Ende. Die resultierende Liste enthält neben den Spaltprodukten auch das Trennzeichen sep. |
Die Methoden split und rsplit zerteilen einen String in seine Wörter und geben diese als Liste zurück. Dabei gibt der Parameter sep die Zeichenfolge an, die die Wörter trennt. Mit maxsplit kann die Anzahl der Trennungen begrenzt werden. Geben Sie maxsplit nicht an, wird der String so oft zerteilt, wie sep in ihm vorkommt. Ein gegebenenfalls verbleibender Rest wird als String in die resultierende Liste eingefügt. split beginnt mit dem Teilen am Anfang des Strings, während rsplit am Ende anfängt:
>>> s = "1-2-3-4-5-6-7-8-9-10"
>>> s.split("-")
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
>>> s.split("-", 5)
['1', '2', '3', '4', '5', '6-7-8-9-10']
>>> s.rsplit("-", 5)
['1-2-3-4-5', '6', '7', '8', '9', '10']
Folgen mehrere Trennzeichen aufeinander, werden sie nicht zusammengefasst, sondern es wird jedes Mal erneut getrennt:
>>> s = "1---2-3"
>>> s.split("-")
['1', '', '', '2', '3']
Wird sep nicht angegeben, verhalten sich die beiden Methoden anders. Zuerst werden alle Whitespaces am Anfang und am Ende des Strings entfernt, und anschließend wird der String anhand von Whitespaces zerteilt, wobei dieses Mal aufeinanderfolgende Trennzeichen zu einem zusammengefasst werden:
>>> s = " Irgendein \t\t Satz mit \n\r\t Whitespaces"
>>> s.split()
['Irgendein', 'Satz', 'mit', 'Whitespaces']
Der Aufruf von split ganz ohne Parameter ist sehr nützlich, um einen Text-String in seine Wörter zu spalten, auch wenn diese nicht nur durch Leerzeichen voneinander getrennt sind.
Die Methode splitlines spaltet einen String in seine einzelnen Zeilen auf und gibt eine Liste zurück, die die Zeilen enthält. Dabei werden Unix-Zeilenvorschübe "\n", Windows-Zeilenvorschübe "\r\n" und Mac-Zeilenvorschübe "\r" als Trennzeichen benutzt:
>>> s = "Unix\nWindows\r\nMac\rLetzte Zeile"
>>> s.splitlines()
['Unix', 'Windows', 'Mac', 'Letzte Zeile']
Sollen die trennenden Zeilenvorschübe an den Enden der Zeilen erhalten bleiben, muss für den optionalen Parameter keepends der Wert True übergeben werden.
Die Methode partition zerteilt einen String an der ersten Stelle, an der der übergebene Trenn-String sep auftritt, und gibt ein Tupel zurück, das aus dem Teil vor dem Trenn-String, dem Trenn-String selbst und dem Teil danach besteht. Die Methode rpartition arbeitet genauso, nimmt aber das letzte Vorkommen von sep im Ursprungsstring als Trennstelle:
>>> s = "www.rheinwerk-verlag.de/?GPP=openbook"
>>> s.partition(".")
('www', '.', 'rheinwerk-verlag.de')
>>> s.rpartition(".")
('www.rheinwerk-verlag', '.', 'de')
Suchen von Teil-Strings
Um die Position und die Anzahl der Vorkommen eines Strings in einem anderen String zu ermitteln oder Teile eines Strings zu ersetzen, existieren folgende Methoden:
Methode | Beschreibung |
---|---|
s.find(sub, [start, end]) | Sucht den String sub im String s. Die Suche beginnt am String-Anfang. |
s.rfind(sub, [start, end]) | Sucht den String sub im String s. Die Suche beginnt am String-Ende. |
s.index(sub, [start, end]) |
Sucht den String sub im String s. Die Suche beginnt am String-Anfang. Ist sub nicht in s vorhanden, wird eine Exception geworfen. |
s.rindex(sub, [start, end]) |
Sucht den String sub im String s. Die Suche beginnt am String-Ende. Ist sub nicht in s vorhanden, wird eine Exception geworfen. |
s.count(sub, [start, end]) | Zählt die Vorkommen von sub in s. |
Die optionalen Parameter start und end der fünf Methoden dienen dazu, den Suchbereich einzugrenzen. Wenn Sie start bzw. end angeben, wird nur der Teil-String s[start:end] betrachtet.
[»] Hinweis
Zur Erinnerung: Beim Slicing eines Strings s mit s[start:end] wird ein Teil-String erzeugt, der bei s[start] beginnt und das Element s[end] nicht mehr enthält.
Um herauszufinden, ob und gegebenenfalls an welcher Stelle ein bestimmter String in einem anderen vorkommt, bietet Python die Methoden find und index mit ihren Gegenstücken rfind und rindex an. Die Methode find gibt den Index des ersten Vorkommens von sub in s zurück, rfind entsprechend den Index des letzten Vorkommens. Ist sub nicht in s enthalten, geben find und rfind den Wert -1 zurück:
>>> s = "Mal sehen, wo das 'e' in diesem String vorkommt"
>>> s.find("e")
5
>>> s.rfind("e")
29
Die Methoden index und rindex arbeiten auf die gleiche Weise, erzeugen aber einen ValueError, wenn sub nicht in s enthalten ist:
>>> s = "Dieser String wird gleich durchsucht"
>>> s.index("wird")
14
>>> s.index("nicht vorhanden")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: substring not found
Der Grund für diese fast identischen Methoden liegt darin, dass sich Fehlermeldungen unter Umständen eleganter handhaben lassen als ungültige Rückgabewerte.[ 47 ](Sie können die Details in Kapitel 22, »Kapitel 22«, nachlesen. )
Wie oft ein Teil-String in einem anderen enthalten ist, lässt sich mit count ermitteln:
>>> "Fischers Fritze fischt frische Fische".count("sch")
4
Ersetzen von Teil-Strings
Mit den folgenden Methoden lassen sich bestimmte Teile oder Buchstaben von Strings durch andere ersetzen:
Methode | Beschreibung |
---|---|
s.replace(old, new, [count]) | Ersetzt die Vorkommen von old im String s durch new. |
s.lower() | Ersetzt alle Großbuchstaben in s durch entsprechende Kleinbuchstaben. |
s.upper() | Ersetzt alle Kleinbuchstaben in s durch entsprechende Großbuchstaben. |
s.swapcase() | Ersetzt alle Großbuchstaben in s durch entsprechende Kleinbuchstaben und umgekehrt alle Kleinbuchstaben durch entsprechende Großbuchstaben. |
s.capitalize() | Ersetzt den ersten Buchstaben von s durch den entsprechenden Großbuchstaben und alle folgenden Großbuchstaben durch entsprechende Kleinbuchstaben. |
s.casefold() |
Arbeitet ähnlich wie s.lower(), wobei zusätzlich Sonderzeichen ersetzt werden. So liefert etwa "Straße".casefold() das Ergebnis "strasse" zurück. Die Rückgabewerte von s.casefold() sind für Vergleiche zwischen Strings gedacht, bei denen es nicht auf Groß- und Kleinschreibung ankommt. |
s.title() | Ändert die Groß-/Kleinschreibung von s so, dass alle Wörter bis auf den ersten Buchstaben kleingeschrieben werden. |
s.expandtabs( [tabsize]) | Rückt s ein, indem Tabs ("\t") durch Leerzeichen ersetzt werden. |
Die Methode replace gibt einen String zurück, in dem alle Vorkommen von old durch new ersetzt wurden:
>>> falsch = "Python ist nicht toll!"
>>> richtig = falsch.replace("nicht", "richtig")
>>> richtig
'Python ist richtig toll!'
Mit dem Parameter count kann die Anzahl der Ersetzungen begrenzt werden:
>>> s = "Bitte nur die ersten vier e ersetzen"
>>> s.replace("e", "E", 4)
'BittE nur diE ErstEn vier e ersetzen'
Die Methode lower ersetzt alle Großbuchstaben eines Strings durch die entsprechenden Kleinbuchstaben und gibt den Ergebnis-String zurück:
>>> s = "ERST GANZ GROSS UND DANN GANZ KLEIN!"
>>> s.lower()
'erst ganz gross und dann ganz klein!'
Mit upper erreichen Sie genau den umgekehrten Effekt.
Die Methode swapcase ändert die Groß- bzw. Kleinschreibung aller Buchstaben eines Strings, indem sie alle Großbuchstaben durch die entsprechenden Kleinbuchstaben und umgekehrt ersetzt:
>>> s = "wENN MAN IM dEUTSCHEN ALLE wORTE SO SCHRIEBE ..."
>>> s.swapcase()
'Wenn man im Deutschen alle Worte so schriebe ...'
Die Methode capitalize gibt eine Kopie des Ursprungsstrings zurück, wobei das erste Zeichen – sofern möglich – in einen Großbuchstaben umgewandelt wurde:
>>> s = "alles klein ... noch ;)"
>>> s.capitalize()
'Alles klein ... noch ;)'
Die Methode title erzeugt einen String, bei dem alle Wörter groß-, aber ihre restlichen Buchstaben kleingeschrieben sind, wie dies im Englischen bei Titeln üblich ist:
>>> s = "nOch BIn iCH eheR weNiGEr alS TITeL gEeiGNEt"
>>> s.title()
'Noch Bin Ich Eher Weniger Als Titel Geeignet'
Mit expandtabs können Sie alle Tabulatorzeichen ("\t") eines Strings durch Leerzeichen ersetzen lassen. Der optionale Parameter tabsize gibt dabei an, wie viele Leerzeichen für einen Tabulator eingefügt werden sollen. Ist tabsize nicht angegeben, werden acht Leerzeichen verwendet:
>>> s = ("\tHier kann Quellcode stehen\n" +
... "\t\tEine Ebene weiter unten")
>>> print(s.expandtabs(4))
Hier kann Quellcode stehen
Eine Ebene weiter unten
Entfernen bestimmter Zeichen am Anfang oder am Ende von Strings
Die strip-Methoden ermöglichen es, unerwünschte Zeichen am Anfang oder am Ende eines Strings zu entfernen:
Methode | Beschreibung |
---|---|
s.strip([chars]) | Entfernt bestimmte Zeichen am Anfang und am Ende des Strings s. |
s.lstrip([chars]) | Entfernt bestimmte Zeichen am Anfang des Strings s. |
s.rstrip([chars]) | Entfernt bestimmte Zeichen am Ende des Strings s. |
Die Methode strip entfernt unerwünschte Zeichen auf beiden Seiten des Strings. Die Methode lstrip entfernt nur die Zeichen auf der linken Seite und rstrip nur die Zeichen auf der rechten.
Für den optionalen Parameter chars können Sie einen String übergeben, der die Zeichen enthält, die entfernt werden sollen. Geben Sie chars nicht an, werden alle Whitespaces gelöscht:
>>> s = " \t\n \rUmgeben von Whitespaces \t\t\r"
>>> s.strip()
'Umgeben von Whitespaces'
>>> s.lstrip()
'Umgeben von Whitespaces \t\t\r'
>>> s.rstrip()
' \t\n \rUmgeben von Whitespaces'
Um beispielsweise alle umgebenden Ziffern zu entfernen, könnten Sie so vorgehen:
>>> ziffern = "0123456789"
>>> s = "3674784673546Versteckt zwischen Zahlen3425923935"
>>> s.strip(ziffern)
'Versteckt zwischen Zahlen'
Ausrichten von Strings
Die folgenden Methoden erzeugen einen String mit einer vorgegebenen Länge und richten den Ursprungsstring darin auf eine bestimmte Weise aus:
Methode | Beschreibung |
---|---|
s.center(width, [fillchar]) | Zentriert s im resultierenden String. |
s.ljust(width, [fillchar]) | Richtet s im resultierenden String linksbündig aus. |
s.rjust(width, [fillchar]) | Richtet s im resultierenden String rechtsbündig aus. |
s.zfill(width) | Richtet s rechtsbündig aus, indem links mit Nullen aufgefüllt wird. |
Mit dem Parameter width geben Sie die gewünschte Länge des neuen Strings an. Die Methode center zentriert s im neuen String, ljust richtet s linksbündig aus, rjust richtet s rechtsbündig aus. Der optionale Parameter fillchar der drei ersten Methoden muss ein String der Länge eins sein und gibt das Zeichen an, das zum Auffüllen bis zur übergebenen Länge verwendet werden soll. Standardmäßig werden Leerzeichen zum Füllen benutzt:
>>> s = "Richte mich aus"
>>> s.center(50)
' Richte mich aus '
>>> s.ljust(50)
'Richte mich aus '
>>> s.rjust(50, "-")
'-----------------------------------Richte mich aus'
Ist die Länge von s größer als der Wert des Parameters width, wird eine Kopie von s zurückgegeben, da in diesem Fall nicht genügend Raum zum Ausrichten vorhanden ist.
Die Methode zfill ist ein Spezialfall von rjust und für Strings gedacht, die numerische Werte enthalten. Ein Aufruf der Methode zfill erzeugt einen String der Länge width, in dem der Ursprungsstring rechts ausgerichtet ist und die linke Seite mit Nullen aufgefüllt wurde:
>>> "13.37".zfill(20)
'00000000000000013.37'
String-Tests
Die folgenden Methoden geben einen Wahrheitswert zurück, der aussagt, ob der Inhalt des Strings eine bestimmte Eigenschaft hat. Mit islower beispielsweise prüfen Sie, ob alle Buchstaben in s Kleinbuchstaben sind.
Methode | Beschreibung |
---|---|
s.isalnum() | True, wenn alle Zeichen in s Buchstaben oder Ziffern sind |
s.isalpha() | True, wenn alle Zeichen in s Buchstaben sind |
s.isdigit() | True, wenn alle Zeichen in s Ziffern sind |
s.islower() | True, wenn alle Buchstaben in s Kleinbuchstaben sind |
s.isupper() | True, wenn alle Buchstaben in s Großbuchstaben sind |
s.isspace() | True, wenn alle Zeichen in s Whitespaces sind |
s.istitle() | True, wenn alle Wörter in s großgeschrieben sind |
s.startswith(prefix, [start, end]) | True, wenn s mit dem String prefix beginnt |
s.endswith(suffix, [start, end]) | True, wenn s mit dem String suffix endet |
Da sich die ersten sieben Methoden sehr ähneln, soll ein Beispiel an dieser Stelle ausreichen:
>>> s = "1234abcd"
>>> s.isdigit()
False
>>> s.isalpha()
False
>>> s.isalnum()
True
Um zu prüfen, ob ein String mit einer bestimmten Zeichenkette beginnt oder endet, dienen die Methoden startswith bzw. endswith:
Die optionalen Parameter start und end begrenzen dabei – wie schon bei den Suchen-und-Ersetzen-Methoden – die Abfrage auf den Bereich s[start:end]:
>>> s = "www.rheinwerk-verlag.de/?GPP=openbook"
>>> s.startswith("www.")
True
>>> s.endswith(".de")
True
>>> s.startswith("rheinwerk", 4)
True
Verkettung von Elementen in sequenziellen Datentypen
Eine häufige Aufgabe ist es, eine Liste von Strings mit einem Trennzeichen zu verketten. Für diesen Zweck stellt Python die Methode join zur Verfügung:
Methode | Beschreibung |
---|---|
s.join(seq) | Verkettet die Elemente der Sequenz seq zu einem neuen String, wobei s als Trennzeichen dient. |
Der Parameter seq ist dabei ein beliebiges iterierbares Objekt, dessen Elemente alle Strings sein müssen. Die Elemente von seq werden mit s als Trennzeichen verkettet. Im folgenden Beispiel werden mehrere Namen, durch Komma getrennt, verkettet:
>>> kontaktliste = ["Fix", "Foxy", "Lupo", "Dr. Knox"]
>>> ", ".join(kontaktliste)
'Fix, Foxy, Lupo, Dr. Knox'
Wird für seq ein String übergeben, ist das Ergebnis die Verkettung aller Buchstaben, jeweils durch s voneinander getrennt:
>>> satz = "Stoiber-Satz"
>>> "...ehm...".join(satz)
'S...ehm...t...ehm...o...ehm...i...ehm...b...ehm...e...ehm...r...ehm...-...ehm...S...ehm...a...ehm...t...ehm...z'
Die Methode join wird oft angewendet, um die Elemente einer Sequenz ohne ein Trennzeichen zu verketten. In diesem Fall ruft man die Methode join des leeren Strings auf:
>>> "".join(["www", ".", "rheinwerk-verlag", ".", "de"])
'www.rheinwerk-verlag.de'
Im nächsten Abschnitt beschäftigen wir uns mit dem Thema String-Formatierung.
13.4.3 Formatierung von Strings
Oft möchte man seine Bildschirmausgaben auf bestimmte Weise anpassen. Um beispielsweise eine dreispaltige Tabelle von Zahlen anzuzeigen, müssen – abhängig von der Länge der Zahlen – Leerzeichen eingefügt werden, damit die einzelnen Spalten untereinander angezeigt werden. Eine Anpassung der Ausgabe ist auch nötig, wenn Sie einen Geldbetrag ausgeben möchten, der in einer float-Instanz gespeichert ist, die mehr als zwei Nachkommastellen besitzt.
Für die Lösung solcher Probleme gibt es seit Python 3.0 die format-Methode des Datentyps str. Mithilfe von format können Sie in einem String Platzhalter durch bestimmte Werte ersetzen lassen. Diese Platzhalter sind durch geschweifte Klammern eingefasst und können sowohl Zahlen als auch Zeichenketten sein. Im folgenden Beispiel lassen wir die Platzhalter {0} und {1} durch zwei Zahlen ersetzen:
>>> "Es ist {0}:{1} Uhr".format(13, 37)
'Es ist 13:37 Uhr'
Wenn Zahlen als Platzhalter verwendet werden, müssen sie fortlaufend durchnummeriert sein, bei 0 beginnend. Sie werden dann der Reihe nach durch die Parameter ersetzt, die der format-Methode übergeben wurden – der erste Parameter ersetzt {0}, der zweite Parameter ersetzt {1} und so fort.
Es ist auch möglich, diese Nummerierung implizit vornehmen zu lassen, indem nichts zwischen die geschweiften Klammern geschrieben wird. Python nummeriert die Platzhalter dann automatisch, bei 0 beginnend:
>>> "Es ist {}:{} Uhr".format(13, 37)
'Es ist 13:37 Uhr'
Wie bereits erwähnt, können auch Namen als Platzhalter verwendet werden. In diesem Fall müssen Sie die Werte als Schlüsselwortparameter an die format-Methode übergeben:
>>> "Es ist {stunde}:{minute} Uhr".format(stunde=13, minute=37)
'Es ist 13:37 Uhr'
Als Namen für die Platzhalter kommen dabei alle Zeichenketten infrage, die auch als Variablennamen in Python verwendet werden können. Insbesondere sollten Ihre Platzhalternamen nicht mit Ziffern beginnen, da sonst versucht wird, sie als Ganzzahlen zu interpretieren.
Man kann auch nummerierte Platzhalter mit symbolischen Platzhaltern mischen:
>>> "Es ist {stunde}:{0} Uhr".format(37, stunde=13)
'Es ist 13:37 Uhr'
Dieses Mischen symbolischer und nummerierter Platzhalter funktioniert auch zusammen mit der impliziten Nummerierung. Python nummeriert die Platzhalter dann automatisch, bei 0 beginnend.
>>> "{h}g Hefe, {}g Mehl, {w}ml Wasser, {}g Salz".format(
... 50, 400, h=5, w=100)
'5g Hefe, 50g Mehl, 100ml Wasser, 400g Salz'
Anstelle der Zahlenwerte, die in den bisherigen Beispielen verwendet wurden, können Sie allgemein beliebige Objekte als Werte verwenden, sofern sie in einen String konvertiert werden können.[ 48 ](Näheres dazu, wie diese Konvertierung intern abläuft und beeinflusst werden kann, finden Sie in Abschnitt 21.7, »Abschnitt 21.7«. ) Im folgenden Code-Schnipsel werden verschiedene Datentypen an die format-Methode übergeben:
>>> "Liste: {0}, String: {string}, Komplexe Zahl: {1}".format(
... [1,2], 13 + 37j, string="Hallo Welt")
'Liste: [1, 2], String: Hallo Welt, Komplexe Zahl: (13+37j)'
Um denselben Wert mehr als einmal in einen String einzufügen, können Sie Platzhalter mehrfach verwenden.
>>> "{h}{em} Hefe, {}{em} Mehl, {w}{ev} Wasser, {}{em} Salz".format(
... 50, 400, h=5, w=100, em='g', ev='ml')
'5g Hefe, 50g Mehl, 100ml Wasser, 400g Salz'
Dabei stehen em und ev für »Einheit für Masse« und »Einheit für Volumen«.
Möchten Sie verhindern, dass eine geschweifte Klammer als Begrenzung eines Platzhalters interpretiert wird, setzen Sie zwei Klammern hintereinander. Im Ergebnis werden diese doppelten Klammern durch einfache ersetzt:
>>> "Unformatiert: {{KeinPlatzhalter}}. Formatiert: {v}.".format(
... v="nur ein Test")
'Unformatiert: {KeinPlatzhalter}. Formatiert: nur ein Test.'
[»] Hinweis
Die Methode format ist seit Python 3 der Standard für String-Formatierungen und löst den Formatierungsoperator % ab. Daher sollten Sie in Ihren Programmen nach Möglichkeit die Methode format für die String-Formatierung verwenden. Für die Datentypen bytes und bytearray kann der Operator % seit Python 3.5 verwendet werden. Näheres entnehmen Sie bitte Pythons Onlinedokumentation.
Um Python-Programmierern den Umstieg zu erleichtern, wurde die format-Methode schon in Python 2.6 hinzugefügt. Sie können format also auch dann einsetzen, wenn Ihre Programme mit Python 2.6 funktionieren sollen. Näheres zu den Unterschieden zwischen Python 2 und Python 3 erfahren Sie in Kapitel 43, »Von Python 2 nach Python 3«.
Zusätzlich wurde mit Python 3.6 ein neues String-Literal für Format-Strings eingeführt. Wird einem String-Literal ein f vorangestellt, werden von geschweiften Klammern eingeschlossene Teile nach bestimmten Regeln ersetzt:
>>> name, vorname = "Ostermann", "Lina"
>>> f"Hallo Frau {vorname} {name}!"
'Hallo Frau Lina Ostermann!'
Diese Erweiterung ist noch recht neu und außerdem sehr ähnlich zu dem, was in den folgenden Abschnitten besprochen wird. Deshalb belassen wir es an dieser Stelle bei einem Verweis auf Pythons Onlinedokumentation und das PEP 498 .
Zugriff auf Attribute und Elemente
Neben dem einfachen Ersetzen von Platzhaltern ist es auch möglich, in dem Format-String auf Attribute des übergebenen Wertes zuzugreifen. Dazu schreiben Sie das gewünschte Attribut, durch einen Punkt abgetrennt, hinter den Namen des Platzhalters, genau wie dies beim normalen Attributzugriff in Python funktioniert.
Das folgende Beispiel gibt auf diese Weise den Imaginär- und Realteil einer komplexen Zahl aus:
>>> c = 15 + 20j
>>> "Realteil: {0.real}, Imaginaerteil: {0.imag}".format(c)
'Realteil: 15.0, Imaginaerteil: 20.0'
Wie Sie sehen, funktioniert der Attributzugriff auch bei nummerierten Platzhaltern.
Neben dem Zugriff auf Attribute des zu formatierenden Wertes kann auch der []-Operator verwendet werden. Damit können beispielsweise gezielt Elemente einer Liste ausgegeben werden:
>>> l = ["Ich bin der Erste!", "Nein, ich bin der Erste!"]
>>> "{liste[1]}. {liste[0]}".format(liste=l)
'Nein, ich bin der Erste!. Ich bin der Erste!'
Auch wenn Sie zu diesem Zeitpunkt keine weiteren Datentypen kennengelernt haben, die den []-Operator unterstützen, ist die Anwendung in Format-Strings nicht auf sequenzielle Datentypen beschränkt. Insbesondere ist diese Art von Zugriff bei Dictionarys interessant, die wir in Abschnitt 14.1 behandeln werden.[ 49 ](Bitte beachten Sie, dass die Schlüssel des Dictionarys nicht in Hochkommata eingeschlossen werden, auch wenn es sich dabei um Strings handeln sollte. Dies führt dazu, dass beispielsweise der Schlüssel ":-]" nicht in einem Format-String verwendet werden kann. )
Sowohl der Zugriff auf Attribute als auch der Operator [] funktionieren auch für die implizite Nummerierung von Platzhaltern:
>>> "Attribut: {.imag}, Listenelement: {[1]}".format(
... 1+4j, [1,2,3])
'Attribut: 4.0, Listenelement: 2'
Im Folgenden erfahren Sie, wie Sie die Ersetzung selbst beeinflussen können.
Formatierung der Ausgabe
Bisher haben wir mithilfe von format nur Platzhalter durch bestimmte Werte ersetzt, ohne dabei festzulegen, nach welchen Regeln die Ersetzung vorgenommen wird. Um dies zu erreichen, können Formatangaben (engl. format specifier) durch einen Doppelpunkt getrennt vom Namen des Platzhalters angegeben werden. Um beispielsweise eine Gleitkommazahl auf zwei Nachkommastellen gerundet auszugeben, benutzt man die Formatangabe .2f:
>>> "Betrag: {:.2f} Euro".format(13.37690)
'Betrag: 13.38 Euro'
Die Wirkung der Formatangaben hängt von dem Datentyp ab, der als Wert für den jeweiligen Platzhalter übergeben wird. Wir werden im Folgenden die Formatierungsmöglichkeiten für Pythons eingebaute Datentypen unter die Lupe nehmen.
Beachten Sie, dass sämtliche Formatierungsangaben optional und unabhängig voneinander sind. Sie können deshalb auch einzeln auftreten.
Bevor wir uns mit den Formatierungsmöglichkeiten im Detail beschäftigen, möchten wir Ihnen kurz den prinzipiellen Aufbau einer Formatangabe zeigen:
[[fill]align][sign][#][0][minimumwidth][,][.precision][type]
Die eckigen Klammern bedeuten dabei, dass es sich bei ihrem Inhalt um optionale Angaben handelt. Im Folgenden werden alle dieser Felder einzeln diskutiert.
Minimale Breite festlegen – minimumwidth
Wird als Formatangabe eine einfache Ganzzahl verwendet, legt sie die minimale Breite fest, die der ersetzte Wert einnehmen soll. Möchten Sie beispielsweise eine Tabelle mit Namen ausgeben und sicherstellen, dass alles bündig untereinandersteht, erreichen Sie dies folgendermaßen:
f = "{:15}|{:15}"
print(f.format("Vorname", "Nachname"))
print(f.format("Florian", "Kroll"))
print(f.format("Lina", "Ostermann"))
print(f.format("Sven", "Bisdorff"))
print(f.format("Kaddah", "Hotzenplotz"))
In diesem Miniprogramm formatieren wir die beiden Platzhalter 0 und 1 mit einer Breite von jeweils 15 Zeichen. Die Ausgabe sieht damit folgendermaßen aus:
Vorname |Nachname
Florian |Kroll
Lina |Ostermann
Sven |Bisdorff
Kaddah |Hotzenplotz
Sollte ein Wert länger sein als die minimale Breite, wird die Breite des eingefügten Wertes an den Wert angepasst und nicht etwa abgeschnitten:
>>> "{lang:2}".format(lang="Ich bin laenger als zwei Zeichen!")
'Ich bin laenger als zwei Zeichen!'
Wie bereits gesagt, sind sämtliche Formatierungsangaben optional. Dies gilt insbesondere für die minimale Breite. Wenn also im Folgenden davon gesprochen wird, dass eine Angabe zwischen zwei anderen steht oder Ähnliches, soll damit nur deutlich gemacht werden, wie die einzelnen Formatierungsangaben relativ zueinander stehen müssen, wenn sie denn angegeben sind.
Ausrichtung bestimmen – align
Wenn Sie die minimale Breite eines Feldes angeben, können Sie die Ausrichtung des Wertes bestimmen, falls er – wie es im ersten der beiden oben genannten Beispiele der Fall war – nicht die gesamte Breite ausfüllt.
Um beispielsweise einen Geldbetrag wie üblich rechts auszurichten, setzen Sie vor die minimale Breite ein >-Zeichen:
>>> "Endpreis: {sum:>5} Euro".format(sum=443)
'Endpreis: 443 Euro'
Insgesamt gibt es vier Ausrichtungsarten, die in Tabelle 13.17 aufgeführt sind.
Zeichen | Bedeutung |
---|---|
< | Der Wert wird linksbündig in den reservierten Platz eingefügt. Dies ist das Standardverhalten, sofern keine Ausrichtung angegeben ist. |
> | Der Wert wird rechtsbündig in den reservierten Platz eingefügt. |
= |
Sorgt dafür, dass bei numerischen Werten das Vorzeichen immer am Anfang des eingefügten Wertes steht und erst danach eine Ausrichtung nach rechts erfolgt (siehe Beispiel unten). Diese Angabe ist ausschließlich bei numerischen Werten sinnvoll und führt bei anderen Datentypen zu einem ValueError. |
^ | Der Wert wird zentriert in den reservierten Platz eingefügt. |
Ein Beispiel soll die nicht ganz intuitive Wirkung der Ausrichtungsart »=« verdeutlichen. Dabei ist die Position des Vorzeichens interessant.
>>> "Temperatur: {:10}".format(-12.5)
'Temperatur: -12.5'
>>> "Temperatur: {:=10}".format(-12.5)
'Temperatur: - 12.5'
Beachten Sie, dass eine Ausrichtungsangabe keinen Effekt hat, wenn der eingefügte Wert genauso lang wie oder länger als die gewünschte minimale Breite ist.
Füllzeichen – fill
Vor der Ausrichtungsangabe kann das Zeichen festgelegt werden, mit dem die überschüssigen Zeichen beim Ausrichten aufgefüllt werden sollen. Standardmäßig wird dafür das Leerzeichen verwendet. Es kann aber jedes beliebige Zeichen eingesetzt werden:
>>> "{text:-^25}".format(text="Hallo Welt")
'-------Hallo Welt--------'
Hier wurde der String "Hallo Welt" zentriert und von Minuszeichen umgeben eingefügt.
Behandlung von Vorzeichen – sign
Zwischen der Angabe für die minimale Breite und der Ausrichtungsangabe können Sie festlegen, wie mit dem Vorzeichen eines numerischen Wertes verfahren werden soll. Die drei möglichen Formatierungszeichen zeigt Tabelle 13.18.
Zeichen | Bedeutung |
---|---|
+ | Sowohl bei positiven als auch bei negativen Zahlenwerten wird ein Vorzeichen angegeben. |
- |
Nur bei negativen Zahlen wird das Vorzeichen angegeben. Dies ist das Standardverhalten. |
(Leerzeichen) Mit dem Leerzeichen sorgen Sie dafür, dass bei positiven Zahlenwerten anstelle eines Vorzeichens eine Leerstelle eingefügt wird. Negative Zahlen erhalten bei dieser Einstellung ein Minus als Vorzeichen. |
Wir demonstrieren die Behandlung von Vorzeichen an ein paar einfachen Beispielen:
>>> "Kosten: {:+}".format(135)
'Kosten: +135'
>>> "Kosten: {:+}".format(-135)
'Kosten: -135'
>>> "Kosten: {:-}".format(135)
'Kosten: 135'
>>> "Kosten: {: }".format(135)
'Kosten: 135'
>>> "Kosten: {: }".format(-135)
'Kosten: -135'
Wie schon erwähnt, ist die Ausrichtungsangabe = erst bei der Verwendung mit Vorzeichen sinnvoll:
>>> "Kosten: {:=+10}".format(-135)
'Kosten: - 135'
Wie Sie sehen, wird im oben genannten Beispiel das Minuszeichen am Anfang des reservierten Platzes eingefügt und erst danach die Zahl 135 nach rechts ausgerichtet.
Zahlendarstellungstypen – type
Um bei Zahlenwerten die Ausgabe weiter anpassen zu können, gibt es verschiedene Ausgabetypen, die ganz am Ende der Formatangabe eingefügt werden. Beispielsweise werden mit der Typangabe b Ganzzahlen in Binärschreibweise ausgegeben:
>>> "Lustige Bits: {:b}".format(109)
'Lustige Bits: 1101101'
Insgesamt bietet Python für Ganzzahlen acht mögliche Typangaben, die im Folgenden tabellarisch aufgelistet sind.
Zeichen | Bedeutung |
---|---|
b | Die Zahl wird in Binärdarstellung ausgegeben. |
c |
Die Zahl wird als Unicode-Zeichen interpretiert. Näheres zum Thema Unicode finden Sie in Abschnitt 13.4.4, »Zeichensätze und Sonderzeichen«. |
d | Die Zahl wird in Dezimaldarstellung ausgegeben. Dies ist das Standardverhalten. |
o | Die Zahl wird in Oktaldarstellung ausgegeben. |
x | Die Zahl wird in Hexadezimaldarstellung ausgegeben, wobei für die Ziffern a bis f Kleinbuchstaben verwendet werden. |
X | wie x, aber mit Großbuchstaben für die Ziffern von A bis F |
n | Wie d, aber es wird versucht, das für die Region übliche Zeichen zur Trennung von Zahlen zu verwenden (zum Beispiel Tausendertrennung durch einen Punkt). |
Es gibt noch einen alternativen Modus für die Ausgabe von Ganzzahlen, den Sie aktivieren, indem Sie zwischen die minimale Breite und das Vorzeichen eine Raute # schreiben. In diesem Modus werden die Ausgaben in Zahlensystemen mit anderer Basis als 10 durch entsprechende Präfixe gekennzeichnet:
>>> "{:#b} vs. {:b}".format(109, 109)
'0b1101101 vs. 1101101'
>>> "{:#o} vs. {:o}".format(109, 109)
'0o155 vs. 155'
>>> "{:#x} vs. {:x}".format(109, 109)
'0x6d vs. 6d'
Auch für Gleitkommazahlen existieren diverse Ausgabetypen, die Tabelle 13.20 auflistet.
Zeichen | Bedeutung |
---|---|
e | Die Zahl wird in wissenschaftlicher Schreibweise ausgegeben, wobei ein kleines »e« zur Trennung von Mantisse und Exponent verwendet wird. |
E | wie e, nur mit großem »E« als Trennzeichen |
f | Die Zahl wird als Dezimalzahl mit Dezimalpunkt ausgegeben. |
g | Die Zahl wird, wenn sie nicht zu lang ist, wie bei f ausgegeben. Für zu lange Zahlen wird automatisch der e-Typ verwendet. |
G | wie g, nur dass für zu lange Zahlen der E-Typ verwendet wird |
n | Wie g, aber es wird versucht, ein an die Region angepasstes Trennzeichen zu verwenden. |
% | Der Zahlenwert wird zuerst mit hundert multipliziert und dann ausgegeben, gefolgt von einem Prozentzeichen. |
(keine Angabe) | Wie g, aber es wird mindestens eine Nachkommastelle angegeben. |
Das folgende Beispiel veranschaulicht die Formatierungen für Gleitkommazahlen:
>>> "{zahl:e}".format(zahl=123.456)
'1.234560e+02'
>>> "{zahl:f}".format(zahl=123.456)
'123.456000'
>>> "{zahl:n}".format(zahl=123.456)
'123.456'
>>> "{zahl:%}".format(zahl=0.75)
'75.000000%'
Genauigkeit bei Gleitkommazahlen – precision
Es ist möglich, die Anzahl der Nachkommastellen bei der Ausgabe von Gleitkommazahlen festzulegen. Dazu schreiben Sie die gewünschte Anzahl, durch einen Punkt abgetrennt, zwischen die minimale Länge und den Ausgabetyp, wie wir es schon in unserem Einleitungsbeispiel gemacht haben:
>>> "Betrag: {:.2f} Euro".format(13.37690)
'Betrag: 13.38 Euro'
Die überschüssigen Nachkommastellen werden bei der Formatierung nicht abgeschnitten, sondern gerundet.
Beachten Sie, dass in diesem Beispiel die minimale Länge nicht angegeben wurde und dass deshalb die Formatangabe mit einem Punkt beginnt.
Als letzte Formatierungsmöglichkeit kann eine 0 direkt vor der minimalen Breite eingefügt werden. Diese Null bewirkt, dass der überschüssige Platz mit Nullen aufgefüllt und das Vorzeichen am Anfang des reservierten Platzes eingefügt wird. Damit ist dieser Modus gleichwertig mit der Ausrichtungsart = und dem Füllzeichen 0:
>>> "Es gilt {z1:05} = {z2:0=5}.".format(z1=23, z2=23)
'Es gilt 00023 = 00023.'
Tausendertrennung – die Optionen »,« und »_«
Wird die Option »,« gesetzt, werden Tausenderblöcke durch ein Komma voneinander getrennt. Mit »_« kann der Unterstrich als Tausendertrennzeichen gesetzt werden:
>>> "Viel Geld: {:,d}".format(12345678900)
'Viel Geld: 12,345,678,900'
>>> "Viel Geld: {:_d}".format(12345678900)
'Viel Geld: 12_345_678_900'
13.4.4 Zeichensätze und Sonderzeichen
Bisher haben wir uns der Einfachheit halber nur mit Strings beschäftigt, die keine Sonderzeichen (wie Umlaute oder das Eurozeichen) enthalten. Die Besonderheiten beim Umgang mit solchen Zeichen liegen zum Teil an der Entwicklung der Zeichencodierung. Deshalb werden wir diese im Folgenden kurz umreißen.
Zuerst müssen wir eine Vorstellung davon entwickeln, wie ein Computer intern mit Zeichenketten umgeht. Generell lässt sich sagen, dass der Computer eigentlich überhaupt keine Zeichen kennt, da sich in seinem Speicher nur Zahlen befinden. Um trotzdem Bildschirmausgaben zu produzieren oder andere Operationen mit Zeichen durchzuführen, hat man deshalb Übersetzungstabellen, die sogenannten Codepages (dt. »Zeichensatztabellen«), definiert, die jedem Buchstaben eine bestimmte Zahl zuordnen. Der bekannteste und wichtigste Zeichensatz ist durch die ASCII-Tabelle[ 50 ](»American Standard Code for Information Interchange« (dt. »Amerikanische Standardcodierung für den Informationsaustausch«) ) festgelegt.
Durch diese Zuordnung werden neben den Buchstaben und Ziffern auch Satz- und einige Sonderzeichen abgebildet. Außerdem existieren nicht druckbare Steuerzeichen wie der Tabulator oder der Zeilenvorschub. Die ASCII-Tabelle ist eine 7-Bit-Zeichencodierung, was bedeutet, dass von jedem Buchstaben 7 Bit Speicherplatz belegt werden. Es können also 27 = 128 verschiedene Zeichen abgebildet werden. Die Definition des ASCII-Zeichensatzes orientiert sich am Alphabet der englischen Sprache, das insbesondere keine Umlaute wie »ä« oder »ü« enthält. Um auch solche Sonderzeichen in Strings abspeichern zu können, erweiterte man den ASCII-Code, indem man den Speicherplatz für ein Zeichen um ein Bit erhöhte, sodass 28 = 256 verschiedene Zeichen gespeichert werden können. Damit wurde Platz für 128 weitere Sonderzeichen geschaffen. Welche Interpretation konkret für diese weiteren Plätze verwendet wird, legt die verwendete Codepage fest. Welche Codepage verwendet wird, hängt von der Konfiguration des jeweiligen Rechners ab. Insbesondere haben das Betriebssystem und die regionalen Einstellungen Einfluss auf die Auswahl der Codepage.
[»] Hinweis
Unter Windows gibt es unter Umständen Probleme mit der Eingabe von Sonderzeichen in der Eingabeaufforderung. Falls Sie solche Probleme beobachten, können Sie IDLE verwenden, um die betroffenen Beispiele auszuführen.
Pythons bytes-Datentyp implementiert einen solchen 8-Bit-String und ist im Prinzip nichts anderes als eine Kette von Bytes. Um den Zahlenwert eines Zeichens zu ermitteln, gibt es in Python die Built-in Function ord, die als einzigen Parameter einen String der Länge eins erwartet:
>>> ord("j")
106
>>> ord("[")
91
Umgekehrt liefert die Built-in Function chr das zu einem Byte gehörige Zeichen:
>>> chr(106)
'j'
>>> chr(91)
'['
Die Beispiele oben beziehen sich nur auf Zeichen mit Ordnungszahlen, die kleiner als 128 sind und damit noch im ASCII-Bereich liegen. Interessanter ist das folgende Beispiel:
>>> ord("ä")
228
Auf dem Computer, der dieses Beispiel ausgeführt hat, läuft ein Betriebssystem, das die Codepage mit dem Namen »iso-8859-15« verwendet. Die Codepage »iso-8859-15« deckt alle wichtigen Zeichen für Westeuropa ab, z. B. Umlaute, Akzente und das Eurozeichen. Wenn Sie das Beispiel ausführen und eine andere Zahl als 228 auf dem Bildschirm sehen, liegt das daran, dass Ihr Computer eine andere Codepage verwendet.
Wir haben uns bereits während der Einführung zu Strings mit Escape-Sequenzen beschäftigt. In Bezug auf Sonderzeichen spielen sie eine zentrale Rolle:
>>> '\xdcberpr\xfcfung der \xc4nderungen'
'Überprüfung der Änderungen'
Was auf den ersten Blick kryptisch erscheint, hat eine einfache Struktur: Wie Sie bereits wissen, wird durch den Backslash \ innerhalb von String-Literalen eine Escape-Sequenz eingeleitet. Die Escape-Sequenz mit der Kennung x ermöglicht es, einzelne Bytes in str-Instanzen direkt zu codieren. Sie erwartet eine zweistellige Hexadezimalzahl als Parameter, die direkt hinter das x geschrieben wird. Der Wert dieses Parameters gibt den Zahlenwert des Bytes an, im Beispiel also 0xdc = 220 ("Ü"), 0xfc = 252 ("ü") und 0xc4 = 196 ("Ä"). Die Darstellungen hat Python der aktuellen Codepage entnommen, in der sie genau den angegebenen Zeichen entsprechen:
>>> print(chr(220), chr(252), chr(196))
Ü ü Ä
Diese Codierung von Sonderzeichen hat den Vorteil, dass der Quelltext nur aus normalen ASCII-Zeichen besteht und beim Abspeichern und Verteilen nicht mehr auf die verwendete Codepage geachtet werden muss.
Allerdings bringt eine solche Codierung zwei wichtige Nachteile mit sich: Zum einen ist die Anzahl möglicher Zeichen auf 256 begrenzt, und zum anderen muss jemand, der einen so codierten String verarbeiten will, wissen, welche Codepage verwendet wurde, weil sich viele Codepages widersprechen. Letzterer Nachteil bedeutet, dass man immer genau wissen muss, mit welcher Codepage gearbeitet wurde, um einen codierten String korrekt zu interpretieren. Dies ist insbesondere beim Datenaustausch oder beim Lesen und Schreiben von Dateien unbequem und fehleranfällig.
Ein wirklicher Mangel ist die Begrenzung der Zeichenanzahl. Stellen Sie sich einen String vor, der eine Ausarbeitung über Autoren aus verschiedenen Sprachräumen mit Originalzitaten enthält: Sie würden aufgrund der vielen verschiedenen Alphabete schnell an die Grenze der 8-Bit-Codierung stoßen und könnten das Werk nicht digitalisieren. Oder stellen Sie sich vor, Sie wollten einen Text in chinesischer Sprache codieren, was durch die über 10.000 Schriftzeichen unmöglich würde.
Ein naheliegender Lösungsansatz für dieses Problem besteht darin, den Speicherplatz pro Zeichen zu erhöhen, was aber neue Nachteile mit sich bringt. Verwendet man beispielsweise 16 Bit für jedes einzelne Zeichen, ist die Anzahl der Zeichen immer noch auf 65.536 begrenzt. Es ist davon auszugehen, dass die Sprachen sich weiterentwickeln werden und somit auch diese Anzahl auf Dauer nicht mehr ausreichen wird.[ 51 ](Es ist tatsächlich so, dass 16 Bit schon heute nicht mehr ausreichen, um alle Zeichen der menschlichen Sprache zu codieren. ) Außerdem würde sich im 16-Bit-Beispiel der Speicherplatzbedarf für einen String verdoppeln, weil für jedes Zeichen doppelt so viele Bits wie bei erweiterter ASCII-Codierung verwendet würden, und das, obwohl ein Großteil aller Texte hauptsächlich aus einer kleinen Teilmenge aller vorhandenen Zeichen besteht. Die einfache Speicherplatzerhöhung für jedes einzelne Zeichen ist also keine wirkliche Lösung, denn das Problem wird irgendwann wieder auftreten, wenn die neu gesetzte Schranke erneut überschritten wird. Außerdem wird unnötig Speicherplatz vergeudet.
Eine langfristige Lösung für das Codierungsproblem wurde schließlich durch den Standard Unicode erarbeitet, der variable Codierungslängen für einzelne Zeichen vorsieht. Im Prinzip ist Unicode eine riesige Tabelle, die jedem bekannten Zeichen eine Zahl, den sogenannten Codepoint, zuweist. Diese Tabelle wird vom Unicode Consortium, einer gemeinnützigen Institution, gepflegt und ständig erweitert. Codepoints werden in der Regel als »U+x« geschrieben, wobei x die hexadezimale Repräsentation des Codepoints ist. Das wirklich Neue an Unicode ist das Verfahren UTF (Unicode Transformation Format), das Codepoints durch Byte-Folgen unterschiedlicher Länge darstellen kann. Es gibt verschiedene dieser Transformationsformate, aber das wichtigste und am weitesten verbreitete ist UTF-8. UTF-8 verwendet bis zu 7 Byte, um ein einzelnes Zeichen zu codieren, wobei die tatsächliche Länge von der Häufigkeit des Zeichens in Texten abhängt. So lassen sich zum Beispiel alle Zeichen des ASCII-Standards mit jeweils einem Byte codieren, das zusätzlich den gleichen Zahlenwert wie die entsprechende ASCII-Codierung des Zeichens hat. Durch dieses Vorgehen wird erreicht, dass jeder mit ASCII codierte String auch gültiger UTF-8-Code ist: UTF-8 ist zu ASCII abwärtskompatibel. Wir interessieren uns an dieser Stelle nicht weiter für die technische Umsetzung von Unicode, sondern vielmehr dafür, wie wir Unicode mit Python nutzen können.
Seit Python 3.0 ist der Umgang mit Unicode wesentlich komfortabler geworden, da eine klare Trennung zwischen Binärdaten (Datentyp bytes) und Textdaten (Datentyp str) eingeführt wurde. Sie müssen sich deshalb nicht mehr so intensiv wie früher mit der Codierung von Zeichen befassen. Dennoch gibt es Situationen, in denen Sie direkt mit der Zeichencodierung in Berührung kommen.
Wie Sie bereits im Beispiel am Anfang gesehen haben, können Sie Sonderzeichen in String-Literalen durch Escape-Sequenzen codieren. Wir haben dabei Escape-Sequenzen verwendet, die mit \x beginnen. Diese Sequenzen sind allerdings nur für Zeichen geeignet, die einen der ersten 256 Codepoints verwenden. Für beliebige Sonderzeichen, wie zum Beispiel das Eurosymbol € (Codepoint 8364, hexadezimal: 0x20ac), gibt es Escape-Sequenzen, die mit \u eingeleitet werden:
>>> s = "\u20ac"
>>> print(s)
€
Der neue Datentyp str eignet sich für die Arbeit mit Text-Strings in Python-Programmen und vereinfacht dabei den Umgang mit internationalen Schriftzeichen. Allerdings gibt es einige Besonderheiten, die bei der Verwendung des neuen str beachtet werden müssen.[ 52 ](Vor allem, wenn Sie den Umgang mit 8-Bit-Strings gewohnt sind, ist hier Vorsicht geboten. ) Unicode abstrahiert von Bytes zu Zeichen, was für den Programmierer angenehmer ist, auf Maschinenebene aber den Nachteil mit sich bringt, dass solche Strings nicht einfach in Byte-Ketten gespeichert werden können. Möchten Sie aber beispielsweise Daten auf der Festplatte ablegen, sie über das Netzwerk versenden oder mit anderen Programmen austauschen, sind Sie auf die Gegebenheiten der Maschine und damit auch die Byte-Ketten beschränkt. Es muss also Möglichkeiten geben, aus einem abstrakten str-String eine konkrete Byte-Folge, also ein bytes-Objekt, zu erzeugen und umgekehrt. Instanzen des Typs str haben eine Methode encode, die als Parameter den Namen der gewünschten Codierung enthält, zum Beispiel "utf8". Das Ergebnis dieser Umwandlung ist eine bytes-Instanz, die die Repräsentation des Strings in der übergebenen Codierung enthält. Um aus einer codierten bytes-Instanz wieder ein str-Objekt zu machen, verwenden wir die Methode decode. Sie erwartet als Parameter den Namen der Codierungsvorschrift, die beim Erzeugen des Strings verwendet wurde:
>>> textstring = "Überprüfung der Änderungen; \u20ac"
>>> textstring
'Überprüfung der Änderungen; €'
>>> utf8bytes = textstring.encode("utf8")
>>> utf8bytes
b'\xc3\x9cberpr\xc3\xbcfung der \xc3\x84nderungen; \xe2\x82\xac'
>>> t = utf8bytes.decode("utf8")
>>> t
'Überprüfung der Änderungen; €'
Im Beispiel erzeugen wir zuerst die str-Instanz textstring, die neben drei direkt eingegebenen Sonderzeichen auch ein maskiertes Eurozeichen enthält. Anschließend nutzen wir die Methode encode, um die UTF-8-Repräsentation von textstring zu ermitteln und mit der Referenz utf8bytes zu verknüpfen. In der Ausgabe von utf8bytes sehen wir, dass für die Codierung der Umlaute zwei und für die des Eurozeichens sogar drei Byte verwendet wurden. Am Ende erhalten wir eine neue str-Instanz, die den gleichen Inhalt hat wie textstring, indem wir utf8bytes mithilfe von decode als UTF-8-String interpretieren.
Innerhalb eines einzelnen Programms ist es wenig sinnvoll, str-Strings erst zu codieren und dann wieder zu decodieren, da man intern bequem mit ihnen arbeiten kann. Wichtig wird die Codierung erst, wenn Sie die enthaltenen Daten senden oder speichern möchten, wobei der Kommunikationskanal oder das Speichermedium nur mit Bytes arbeiten kann.
Das Schema in Abbildung 13.5 veranschaulicht den Transfer von Unicode mithilfe von Codierung und Decodierung.
Angenommen, Programm 1 erzeugt einen String s, der zum Beispiel ein »ü« enthält. Nun soll diese Zeichenkette über eine Netzwerkverbindung, die nur Byte-Folgen übertragen kann, an Programm 2 gesendet werden. Dazu wird s zuerst in sein UTF-8-Äquivalent überführt und dann – wie genau, ist hier nicht wichtig – über das Netzwerk an Programm 2 gesendet, wo es wieder decodiert und anschließend verwendet werden kann.
Als Faustregeln für den Umgang mit den Datentypen str und bytes können Sie sich Folgendes merken:
- Benutzen Sie bytes ausschließlich für Binärdaten.
- Verwenden Sie für alle Textdaten des Programmes str-Instanzen.
- Codieren Sie str-Daten beim Speichern oder beim Datenversand zu anderen Programmen.
- Gewinnen Sie beim Lesen und Empfangen der Daten mit dem entsprechenden Decodierungsverfahren wieder die str-Instanzen zurück.
Wenn Sie diese Regeln konsequent einhalten, kann das Programm mit beliebigen Sonderzeichen umgehen, ohne dass besondere Anpassungen notwendig werden. Dadurch wird nicht nur die Übersetzung, sondern auch der allgemeine Umgang mit Textdaten vereinfacht, weil sich der Programmierer nicht mehr mit den Beschränkungen der Maschine beschäftigen muss. Er muss nur dafür Sorge tragen, dass die Schnittstellen nach außen encodierte Daten bereitstellen.
Codecs
Bis jetzt sind wir nur mit den beiden Codierungsverfahren »iso-8859-15« und »UTF‐8« in Berührung gekommen. Es gibt neben diesen beiden noch eine ganze Reihe weiterer Verfahren, von denen Python viele von Haus aus unterstützt. Jede dieser Codierungen hat in Python einen String als Namen, den Sie der encode-Methode übergeben können. Tabelle 13.21 zeigt exemplarisch ein paar dieser Namen.
Wenn Sie nun versuchen, einen unicode-String mit einem Codierungsverfahren zu encodieren, das nicht für alle in dem String enthaltenen Zeichen geeignet ist, führt dies zu einem Fehler (U+03a9 ist der Codepoint des großen Omega Ω):
>>> s = "\u03a9"
>>> print(s)
Ω
>>> s.encode("cp1252")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'charmap' codec can't encode character '\u03a9' in position 0: character maps to <undefined>
Wie aus dem Beispiel ersichtlich ist, unterstützt die Codepage »cp1252« das Omega-Zeichen nicht, weshalb das Encodieren mit einer Fehlermeldung quittiert wird. Es ergibt sich ein Problem, wenn Sie mit Codierungen arbeiten, die nicht jedes beliebige Zeichen verarbeiten können: Sie können nie sicher sein, dass die beispielsweise vom Benutzer eingegebenen Daten unterstützt werden, und laufen deshalb Gefahr, bei der Verarbeitung das Programm abstürzen zu lassen. Um dieses Problem zu umgehen, bieten die Methoden encode und decode einen optionalen Parameter namens errors an, der die Vorgehensweise in solchen Fehlerfällen definiert. Für errors können die folgenden Werte übergeben werden:
Wert | Bedeutung |
---|---|
"strict" | Standardeinstellung. Jedes nicht codierbare Zeichen führt zu einem Fehler. |
"ignore" | Nicht codierbare Zeichen werden ignoriert. |
"replace" |
Nicht codierbare Zeichen werden durch einen Platzhalter ersetzt: beim Encodieren durch das Fragezeichen "?", beim Decodieren durch das Unicode-Zeichen U+FFFD. |
"xmlcharrefreplace" | Nicht codierbare Zeichen werden durch ihre XML-Entität ersetzt.* (Nur bei encode möglich.) |
"backslashreplace" | Nicht codierbare Zeichen werden durch eine Escape-Sequenz ersetzt. (Nur bei encode möglich.) |
* Dabei handelt es sich um spezielle Formatierungen zur Darstellung von Sonderzeichen in XML-Dateien. Näheres zu XML-Dateien erfahren Sie in Abschnitt 33.2. |
Wir betrachten das letzte Beispiel mit anderen Werten für errors:
>>> s = "\u03a9"
>>> print(s)
Ω
>>> s.encode("cp1252", "replace")
b'?'
>>> s.encode("cp1252", "xmlcharrefreplace")
b'Ω'
>>> s.encode("cp1252", "backslashreplace")
b'\\u03a9'
Damit es erst gar nicht nötig wird, Codierungsprobleme durch diese Hilfsmittel zu umgehen, sollten Sie nach Möglichkeit immer zu allgemeinen Codierungsverfahren wie UTF-8 greifen.
Encoding-Deklaration
Damit Sonderzeichen nicht nur innerhalb von Strings, sondern auch in Kommentaren geschrieben werden dürfen, muss im Kopf einer Python-Programmdatei eine Encoding-Deklaration stehen. Dies ist eine Zeile, die das Encoding kennzeichnet, in dem die Programmdatei gespeichert wurde.
Das ist nur dann wichtig, wenn Sie in der Programmdatei Buchstaben oder Zeichen verwendet haben, die nicht im englischen Alphabet enthalten sind.[ 53 ](Oder Sie speichern Ihre Programme UTF-8-codiert, was Python seit 3.0 standardmäßig erwartet. Wie Sie Ihren Texteditor so konfigurieren können, dass er beim Speichern eine bestimmte Codierung verwendet, entnehmen Sie bitte der Dokumentation des Editors. ) Ein Encoding ermöglicht es dem Python-Interpreter dann, diese Zeichen korrekt zuzuordnen. Eine Encoding-Deklaration steht in der Regel direkt unter der Shebang-Zeile[ 54 ](Die Bedeutung einer Shebang-Zeile wird in Abschnitt 4.1.1 erklärt. ) bzw. in der ersten Zeile der Programmdatei und sieht folgendermaßen aus:
# -*- coding: cp1252 -*-
In diesem Fall wurde das Windows-Encoding cp1252 verwendet.
Beachten Sie, dass aus Gründen der Übersichtlichkeit in keinem Beispielprogramm des Buchs eine Encoding-Deklaration enthalten ist. Das bedeutet aber ausdrücklich nicht, dass der Einsatz einer Encoding-Deklaration grundsätzlich unüblich wäre.