Dieses Kapitel beschäftigt sich unter anderem mit Zeichencodierungen in Swift. Da Sie es aber auf einer Webseite lesen, trifft dieses Thema, das natürlich auch außergewöhnliche Zeichen enthält, auf die Zeichencodierungen von HTML, WordPress, PHP und MySQL. Ein praktisches Beispiel für Codierungsprobleme. Der kleinste gemeinsame Nenner (jedenfalls, wenn ich mir nicht um 1h morgens meine WordPress Installation zerschießen möchte) dieser Systeme ist UTF-8, eine 8-Bit = 2 Byte Unicode Codierung.  Alle Zeichen, deren Codepunkt größer ist als FFFF, sind damit nicht mehr abbildbar. Ich habe solche Zeichen im nun folgenden Text durch das Zeichen ø ersetzt. Wo immer Sie dieses Zeichen also sehen, steht eigentlich ein andreres. Falls Sie das neugierig macht, und sie das korrekte Zeichen sehen möchten, dann probieren Sie doch einfach die Codebeispiele direkt im Playground aus. Und falls Sie gar nicht wissen, wovon ich rede, dann empfehle ich Ihnen das nun folgende Kapitel. Es wird sehr interessant, viel Spaß damit.

Kapitel 2: Grundlagen / 2.5 Einfache Datentypen / 2.5.4 Zeichen und Unicode

Zeichen (engl. characters) sind mehr oder weniger alles, was in einem fließenden Text so verwendet werden kann: Buchstaben, Ziffern (nicht als Zahlenwert, sondern eben nur als Zeichen), das Euro- und das Dollarzeichen, das chinesische Schriftzeichen für Wasser oder ein trauriger Smiley, es gibt extrem viele verschiedene Zeichen.

Um alle Schriftzeichen aller Sprachen zu erfassen, dazu alle möglichen Sonderzeichen, oder genauer: „jedes sinntragende Schriftzeichen oder Textelement aller bekannten Schriftkulturen und Zeichensysteme“ (Wikipedia) wurde Unicode als internationaler Standard ins Leben gerufen. In Unicode sind alle Zeichen quasi durchnummeriert und so bekommt jedes Zeichen einen eindeutigen Code (in Unicode Codepunkt genannt).

Der Zeichentyp in Swift heißt Character und speichert genau ein Unicode Zeichen (indem es schlicht seinen Unicode Codepunkt abspeichert oder eine Folge von Codepunkten, die gemeinsam ein Zeichen repräsentieren). Und es gilt – jedenfalls bis zum Beweis des Gegenteils: Jedes Zeichen, dass in Unicode existiert, kann in Swift als Character gespeichert werden.

Das bedeutet aber nicht automatisch, dass es auch auf dem Bildschirm ausgegeben werden kann. Denn zwischen der Beschreibung eines Zeichens in Unicode und seiner Darstellung auf dem Bildschirm steht der Font. Ein Font liefert die Grafiken für eine meist überschaubare Anzahl von Zeichen. Möchten Sie ein chinesisches Zeichen ausgeben, dann benötigen Sie einen Font für chinesische Zeichen. In OS X und iOS sind viele Fonts bereits vorinstalliert, aber eben nicht alle.

Soll ein Unicode Zeichen auf dem Bildschirm ausgegeben werden, dann schaut OS X bzw. iOS zuerst einmal nach, ob Fonts zur Verfügung stehen, die dieses Zeichen enthalten. Ist das der Fall, wählt es einen davon aus und stellt das Zeichen dar. Findet es keinen passenden Font, dann wird stattdessen meist ein leeres Rechteck dargestellt. Windows und Unix/Linux machen das übrigens genauso.

Sollten Sie also ein Wörterbuch programmieren wollen, um seltene Afrikanische Sprachen ins Deutsche  zu übertragen, könnten Sie hier auf ein Problem stoßen. Aber davon lassen Sie sich in Ihrem Vorhaben nicht stoppen: Im Zweifel können Sie einen speziellen Font als sogenannte Ressource mit in Ihr Programm hineincompilieren.

Nun zur Praxis:

// Beschreibung als Zeichen: €
let euroZeichen: Character = "€"

// Beschreibung Unicode U+0024 = $
let dollarZeichen: Character = "\u{24}"

// Beschreibung als Unicode U+2665 = ♥
let einSchwarzesHerz: Character = "\u{2665}"

// Beschreibung als Unicode U+1F496 = ø
let einFunkelndesHerz: Character = "\u{1F496}"

Zunächst einmal fällt auf, dass  der Typ Character in allen Fällen annotiert ist. Dies liegt daran, dass die Verwendung von Character in der tagtäglichen Programmierung sehr selten ist, so dass Swift als Typ String inferiert, wenn es auf der rechten Seite des Gleichheitszeichens ein Zeichen zwischen zwei Anführungszeichen findet.

Ansonsten ist offensichtlich die direkte Verwendung des Zeichens, wie im Code des Eurozeichens, die einfachste Methode, um einen Character zu beschreiben.

Um die drei anderen Methoden zu verstehen, schauen wir uns einmal genauer an, wie ein Unicode Codepunkt beschaffen ist und blicken dazu in die Historie:

Bevor es nämlich Unicode gab, hatte jedes Land seine eigene Zeichencodierung. Der Speicherplatz war knapp und die Welt weniger international, da kümmerte es die Amerikaner wenig, dass es in Deutschland Umlaute und die Deutschen wenig, dass es in Japan Hiragana gab. Wichtig war, möglichst wenig Speicherplatz zu nutzen.

Die amerikanische Lösung aus dieser Zeit heißt ASCII (American Standard Code for Information Interchange) und benötigt sieben Bit, also weniger als ein Byte. ASCII kann alle Zeichen abbilden, die eine amerikanische Schreibmaschine aus dieser Zeit so drauf hatte: A-Z, a-z und 0-9, dazu ein paar Sonderzeichen wie $, %, & und Steuerzeichen wie LF (Line Feed, Zeilenvorschub), CR (Carriage Return, Wagenücklauf) und Bell (eine Signalglocke).

Das zusätzliche Bit zum vollen Byte ließ es zu, dass sich auf Basis von ASCII auch regionale Zeichencodierungen definieren ließen, wie das in Deutschland bis heute oft verwendete Latin-1 (ISO 8859-1), das ASCII vor allem um viele diakritische Zeichen (Buchstaben, die mit Strichen, Punkten usw. versehen sind) erweiterte, insbesondere um die Umlaute und das ß. Aber natürlich waren Zeichencodierungen auch oft von dem verwendeten Computersystem abhängig.

Entsprechend fehleranfällig war es darum, Zeichen (also auch Wörter und Texte) über die Systeme hinweg zu verwenden. Nur weil etwas während der Programmierung auf dem eigenen Rechner richtig dargestellt wird, muss das auf dem System, auf dem das Programm später läuft, noch lange nicht der Fall sein. Bis heute kann es passieren, dass Frau Schlömpel auf ihrem Flugticket Schloempel heißt oder auf einer Webseite Schl{mpel, weil der Umlaut ö nicht abgebildet werden kann.

Um hier aufzuräumen, wurde 1991 Unicode 1.0 veröffentlicht. Und weil auch Programmierer ein historisches Bewusstsein haben, wurde das erste Byte nach der ASCII-Codierung definiert. Weil es viele Zeichen abzubilden galt, verwendete man insgesamt zwei Byte, so dass 65.536 Zeichen abgebildet werden konnten. Mit der Veröffentlichung von Unicode 2.0 im Jahr 1996 wurde diese Zeichentabelle dann auf 1.114.112 darstellbare Zeichen erweitert, was den Speicherbedarf um vier Bit erhöhte.

Bezüglich Unicode wird fast immer die hexadezimale Schreibweise von Zahlen verwendet: Die von uns im Dezimalsystem verwendeten Ziffern 0-9 werden im Hexadezimalsystem durch die Ziffern 0-F ersetzt, es gibt also 16 Ziffern statt nur 10 (hexa = 6 und dezi = 10). Dies hat den Vorteil, dass ein Byte durch genau zwei Ziffern abgebildet werden kann: FF (hexadezimal) = 255 (dezimal). Zur Anschauung: F (hex) = 15 (dez) und 10 (hex) = 16 (dez), 1F (hex) = 31 (dez) und 20 (hex) = 32 (dez) usw. Ein Unicode Zeichen, ein Codepunkt schreibt sich unabhängig von der verwendeten Programmiersprache (also quasi „in Unicode“) z.B. U+0024 für das Dollarzeichen oder U+2665 für das schwarze Herz (wohlbemerkt: die Zahlen 24 und 2665 sind beide hexadezimal). Wird der durch Unicode 2.0 erweiterte Zeichensatz verwendet, kommt vorn noch eine Ziffer hinzu: U+1F496 codiert ein funkelndes Herz.

Um nun in Swift ein Unicodezeichen durch seinen Codepunkt zu beschreiben, verwendet man \u{n}, wobei n eine hexadezimale Zahl ist, die aus mindestens einer und maximal acht Ziffern besteht.

Im Beispielcode wird nun das Dollarzeichen, ein ASCII-Zeichen, durch \u{24} dargestellt. Das schwarze Herz, ein Unicode 1.0 -Zeichen benötigt vier Ziffern: \u{2665}. Und das am spätesten hinzugekommene funkelnde Herz aus Unicode 2.0 sogar fünf: \u{1F496}.

Dem vorangestellten Backslash \ sind wir im Zusammenhang mit Text schon einmal begegnet: Möchten Sie eine Variable in einen String einbetten, dann können Sie das durch \(variable) tun. Und genauso wie bei der eingebetteten Variable, können Sie auch die Unicode-Beschreibung mit einem Backslash inmitten eines Strings verwenden:

let meineSpielkarte = "Meine Spielkarte ist eine \u{2665}9."
println(meineSpielkarte)

Führt zu der Ausgabe:

Meine Spielkarte ist eine ♥9.

Mit Unicode lässt sich nicht nur jedem Zeichen ein Codepunkt zuzuordnen, es bietet parallel dazu auch noch die Möglichkeit, ein Zeichen aus Teilzeichen zusammenzusetzen. In Unicode bildet ein solches zusammengesetztes Zeichen einen extended grapheme cluster. Swifts Character bildet immer einen extended grapheme cluster ab, auch wenn in den bisherigen Beispielen dieser aus lediglich einem Zeichen bestand. Jedes Zeichen, das aus Teilzeichen besteht, kann also in seine Teilzeichen zerlegt (decomposed) dargestellt oder vorher zusammengefügt (precomposed) abgebildet werden.

Nehmen wir das ö von Frau Schlömpel. Ein ö ist ein o mit zwei Punkten drauf. Das kleine o heißt in Unicode „LATIN SMALL LETTER O“ und hat den Codepunkt U+006F. Die beiden Punkte auf einem Zeichen heißen in Unicode „COMBINING DIAERESIS“ und dieses Zeichen hat den Codepunkt U+0308. Die decomposed Variante von ö ist also U+006F, U+0308. Es gibt das kleine ö aber auch in einer precomposed Variante mit der Unicode Bezeichnung „LATIN SMALL LETTER O WITH diaeresis“ und dem Codepunkt U+00F6.

Im Code sieht das folgendermaßen aus:

let decomposed: Character = "\u{6F}\u{308}"
let precomposed: Character = "\u{F6}"

if decomposed==precomposed {
    println("Es ist dasselbe Zeichen.")
} else {
    println("Es ist nicht dasselbe Zeichen.")
}

Obwohl die Variablen decomposed und precomposed unterschiedliche Inhalte haben (der extended grapheme cluster von decomposed enthält zwei Zeichen und der von precomposed nur eines), codieren sie doch beide dasselbe Zeichen ö. Daher führt das Ausführen dieses Codes zu folgender Ausgabe:

Es ist dasselbe Zeichen.

Extended grapheme clusters können zum Beispiel dafür verwendet werden, um komplexe Schriftzeichen aus ihren Teilzeichen (Radikalen) zusammenzusetzen: So setzt sich das koreanische Zeichen 한 (Unicode U+D55C) aus den Teilzeichen ᄒ (Unicode U+1112), ᅡ (Unicode U+1161) und ᆫ (Unicode U+11AB) zusammen.

Oder es werden einschließende Zeichen verwendet, wie zum Beispiel ein einschließender Kreis (Unicode „COMBINING ENCLOSING CIRCLE“ = U+20DD), der kombiniert mit unserem ö zu dem Zeichen   ö⃝  führt, für das bereits keine precomposed Variante mehr existiert.

Noch skurriler sind beispielsweise regionale Indikatorzeichen. Sie entstehen, indem man regionale Indikator Buchstaben (Unicode: „REGIONAL INDICATOR SYMBOL LETTER X“, wobei X für den Buchstaben steht) miteinander kombiniert, zum Beispiel wird ø (U+1F1FA) mit ø (U+1F1F8) zu ø für die USA oder ø (U+1F1E9) mit ø (U+1F1EA) zu ø für Deutschland.

Oder, um den Nonsens auf die Spitze zu treiben, kann man auf das japanische Kanji für Sonne 日 (U+65E5) die zwei Punkte des ö (U+0308) aufsetzen: 日̈ (Sönne).

Das alles jetzt noch einmal im Code:

let koreanischDecomposed: Character = "\u{1112}\u{1161}\u{11AB}"
let koreanischPrecomposed: Character = "\u{D55C}"

let öImKreis: Character = "\u{F6}\u{20DD}"

let regionalDE: Character = "\u{1F1E9}\u{1F1EA}"
let regionalUS: Character = "\u{1F1FA}\u{1F1F8}"

let sönne: Character = "\u{65E5}\u{0308}"

print(koreanischDecomposed)
print(koreanischPrecomposed)
print(öImKreis)
print(regionalDE)
print(regionalUS)
println(sönne)

Erzeugt folgende Ausgabe:

한한 ö⃝ øø日̈

Vielleicht fragen Sie sich jetzt, warum soviel Aufhebens um den Typ Character gemacht wird, obwohl er – wie ja bereits erwähnt – in der tagtäglichen Programmierung kaum vorkommt. Aber vielleicht kennen Sie die Antwort darauf bereits: Zeichen sind vor allem wichtig, weil sich Strings (Zeichenketten) sich aus ihnen zusammensetzen.

Während Character ein einfacher Datentyp ist, gehört String zu den zusammengesetzten Datentypen, die eben dadurch gekennzeichnet sind – wie der Name es schon sagt – dass sie aus mehreren einfachen Datentypen zusammengesetzt sind. Im Deutschen üblicher ist die englische Bezeichnung für zusammengesetzten Datentypen: Collections. Man sagt in bestem Denglisch: String ist eine Collection of Character.

Und da dem so ist, gilt alles, was für Zeichen gilt, natürlich auch für die im Alltag sehr wichtigen Strings. Doch dazu später mehr.

Ich möchte Ihnen vorschlagen, sich einen Playground und einen Internetbrowser zu öffnen – ein guter Startpunkt für die erste Recherche ist hier wie für vieles Wikipedia – und damit eine Weile in die Welt von Unicode einzutauchen. Was lässt sich darstellen, was lässt sich kombinieren, gibt es dieses Zeichen, usw. Glauben Sie mir, darüber zu lesen ist nur der halbe Spaß.

Kapitel 2.5.4: Zeichen und Unicode

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.