Unicode

(Auszug aus "Reguläre Ausdrücke" von Jeffrey E. F. Friedl)

Um den Begriff »Unicode« gibt es offenbar eine Menge von Missverständnissen. Zuallererst ist Unicode ein Zeichensatz oder eine konzeptuelle Codierung – eine Zuordnung zwischen Zahlen und Zeichen. Zum Beispiel ist dem koreanischen Zeichen 삵 die Nummer 49 333 zugeordnet. Diese Nummer wird Codierungspunkt (code point) genannt und normalerweise als hexadezimale Zahl mit einem »U+« davor angegeben. Die Zahl 49 333 ist C0B5 in hex, also wird 삵 als U+C0B5 bezeichnet. Zu jedem Unicode-Zeichen gehören bestimmte Attribute wie »3 ist eine Ziffer« oder »ê ist ein Kleinbuchstabe, der zugehörige Großbuchstabe ist Ê«.

Bis hierhin wurde noch gar nichts darüber ausgesagt, wie diese Nummern auf einem Computersystem dargestellt werden. Dafür ist eine Reihe von Möglichkeiten vorgesehen, zum Beispiel die UCS-2-Codierung (jedes Zeichen wird mit zwei Bytes codiert), UCS-4 (vier Bytes), UTF-16 (die meisten Zeichen werden mit zwei Bytes codiert, manche mit vier) oder UTF-8 (je nach Art des Zeichens ein bis sechs Bytes). Welche dieser Codierungen ein Programm intern verwendet (falls überhaupt), braucht den Anwender im Allgemeinen nicht zu kümmern. Normalerweise muss er vielleicht externe Daten (aus einer Datei), die in einer bestimmten Codierung vorliegen (ASCII, Latin-1, UTF-8 usw.), in die Codierung bringen, die das Programm benötigt. Unicode-fähige Programme haben normalerweise etliche Konversionsroutinen bereits eingebaut.

Die regulären Ausdrücke in Unicode-fähigen Programmen unterstützen meistens die Metasequenz \uZahl, die auf das angegebene Unicode-Zeichen passt (siehe den Abschnitt Hexadezimale Escapes und Unicode-Escapes: \xZahl, \x{Zahl}, \uZahl, \UZahl, ...). Die Zahl muss dabei meist als vierstellige Hexadezimalzahl angegeben werden; \uC0B5 passt also auf 삵. Wichtig dabei ist, dass die Aussage »\uC0B5 passt auf das Unicode-Zeichen U+C0B5« nichts damit zu tun hat, welche Bytes bei der Mustersuche miteinander verglichen werden. Denn das hängt von der verwendeten Codierung ab, mit der die Unicode-Codierungspunkte intern dargestellt werden. Wenn das Programm intern UTF-8 verwendet, werden für 삵 drei Bytes benötigt. Aber Sie oder irgendein Benutzer eines Unicode-fähigen Programms brauchen sich nur ausnahmsweise darum zu kümmern (beispielsweise bei den preg-Funktionen von PHP mit dem u-Modifikator).

Dennoch interessieren Sie vielleicht ein paar Details aus verwandten Bereichen ...

Zeichen und Zeichenkombinationen

Was ein menschlicher Leser als »Zeichen« bezeichnet, fällt nicht immer mit dem zusammen, was Unicode oder ein Unicode-fähiges Programm darunter versteht. Zum Beispiel werden die meisten Leute à als Zeichen bezeichnen, aber in Unicode kann dieses Symbol aus zwei Zeichen aufgebaut sein, aus der Kombination von U+0061 (a) und U+0300 (`). In Unicode gibt es sogenannte kombinierende Zeichen (combining characters), die auf normale Zeichen, die Grundzeichen (base characters), folgen und mit ihnen zusammen ein neues Zeichen bilden. Das macht die Sache für die Regex-Maschine schwieriger – soll der Punkt nur auf einen Codierungspunkt passen oder auf die ganze Kombination U+0061 plus U+0300?

In der Praxis betrachten die meisten Programme die Begriffe »Zeichen« und »Codierungspunkt« als Synonyme, ob das Zeichen nun aus einer Kombination entsteht oder nicht. Also passt ˹^..$˼ auf à (U+0061 plus U+0300), nicht aber ˹^.$˼.

In Perl und PCRE (und damit in den preg-Funktionen von PHP) gibt es die Metasequenz \X, die etwa das einlöst, was sich die meisten vom Punkt versprechen, nämlich: »passt auf irgendein Zeichen«, und das auch bei Unicode-Zeichen mit beliebig vielen darauf folgenden kombinierenden Zeichen. Mehr dazu erfahren Sie unter Unicode-Sequenz für kombinierende Zeichen: \X.

Man muss sich das auch beim Schreiben von Programmen mit einem Unicode-fähigen Editor vergegenwärtigen. Wenn Sie mit einem solchen Editor in einer Regex ein Å verwenden und der Editor dafür die Kombination von ›A‹ und ›˚‹ benutzt, dann wird das Zeichen kaum auf die Version des Å passen, die nur aus einem einzigen Codierungspunkt besteht (mehr dazu im nächsten Abschnitt). Die Regex-Maschine fasst das als zwei Zeichen auf, in einer Zeichenklasse würden mit ˹[...Å...]˼ zwei Zeichen hinzugefügt, als ob man explizit ˹[...A˚...]˼ angegeben hätte.

Ganz analog bezieht sich ein Quantor, der auf ein solches aus zwei Codierungspunkten bestehendes Å folgt, nicht auf das A, sondern nur auf den zweiten Codierungspunkt, als ob man ˹A˚+˼ schriebe.

Mehrere Codierungspunkte für dasselbe Zeichen

In der Theorie sollte Unicode eine eineindeutige Abbildung von Zeichen und Codierungspunkten sein, aber es gibt eine Reihe von Fällen, in denen ein bestimmtes Zeichen verschieden dargestellt werden kann. Ich hatte im Abschnitt oben geschrieben, dass à durch ein U+0061, gefolgt von einem U+0300, dargestellt wird. Man kann à aber auch durch den einen Codierungspunkt U+00E0 darstellen. Warum ist das so? Diese Doppelspurigkeit soll den Übergang von Latin-1 auf Unicode vereinfachen. Wenn Sie einen Text in Latin-1 haben, der in Unicode verwandelt werden soll, wird sehr wahrscheinlich à (hexadezimal e0 in Latin-1) zu U+00E0, obwohl auch U+0061 plus U+0300 möglich wäre. In den meisten Fällen muss man hier auf beide Möglichkeiten testen. Im java.util.regex-Package von Sun gibt es dafür die Option CANON_EQ, mit der »kanonisch äquivalente« Zeichen als gleich betrachtet werden, auch dann, wenn sie in Unicode verschieden dargestellt werden (siehe Tabelle: Die Regex-Modi bei java.util.regex).

Entfernt verwandt damit ist die Tatsache, dass verschiedene Zeichen sehr ähnlich aussehen können, was bei der Mustersuche Verwirrung hervorruft. Zum Beispiel sieht unser normales großes I (U+0049) dem griechischen Iota (Ι, U+0399) täuschend ähnlich, mit einem Trema bekommen wir Ï und Ï und bereits vier mögliche Unicode-Darstellungen (U+00CF; U+03AA; U+0049 U+0308; U+0399 U+0308). Man müsste also für Ï von Hand auf vier Möglichkeiten testen. Diese Art von Vieldeutigkeit ist häufig.

Ebenfalls nicht selten sind einzelne Unicode-Codierungspunkte, die wie mehrere Zeichen aussehen. In Unicode gibt es beispielsweise das Zeichen »SQUARE HZ« (U+3390), Hz, für die Frequenzeinheit Hertz, das wie die zwei Zeichen ›Hz‹ (U+0048 U+007A) aussieht.

Hz wird natürlich sehr selten verwendet, aber wenn diese Zeichen definiert sind, dann werden sie auch benutzt, und damit werden Programme, die solche Zeichen erkennen sollen, immer komplexer. In eine ähnliche Kategorie gehören beispielsweise das normale Leerzeichen (U+0020) und das geschützte Leerzeichen (no-break space, U+00A0), bei dem nicht umbrochen werden darf – Unicode kennt ungefähr ein Dutzend verschiedene Leerzeichen.

Unicode 3.1+ und Codierungspunkte nach U+FFFF

Mit Unicode Version 3.1 wurden 2001 auch Codierungspunkte mit größeren Nummern als U+FFFF hinzugefügt (das wäre auch bei früheren Unicode-Versionen möglich gewesen, aber bis dahin waren diese Positionen noch nicht belegt). Zum Beispiel hat das Zeichen für den C-Schlüssel Zeichen für den C-Schlüsselin der Musiknotenschrift den Codierungspunkt U+1D121. Ältere Programme, die nur mit Codierungspunkten bis U+FFFF umgehen können, werden damit nicht klarkommen. Bei den meisten Programmiersprachen kann man bei \uZahl ohnehin nur vier hexadezimale Ziffern angeben.

Programme, die diese neuen Codierungspunkte verstehen, verwenden dazu meist die Notation \x{Zahl}, bei der Zahl eine beliebige Anzahl von Stellen aufweisen kann (statt der Notation \uZahl mit genau vier Stellen). Den C-Schlüssel erhält man also mit \x{1D121}.

Das Zeilenende in Unicode

In Unicode ist eine Reihe von Zeichen definiert (in der folgenden Tabelle ist außerdem eine Sequenz von zwei Zeichen aufgeführt), die als Zeilenende-Markierung betrachtet werden können.

Tabelle: Zeichen für das Zeilenende in Unicode.

Zeichen Codierungspunkt Beschreibung
LF U+000A ASCII Line Feed
VT U+000B ASCII Vertical Tab
FF U+000C ASCII Form Feed
CR U+000D ASCII Carriage Return
CR/LF U+000D U+000A ASCII Carriage Return / Line Feed (Kombination)
NEL U+0085 Unicode NEXT LINE
LS U+2028 Unicode LINE SEPARATOR
PS U+2029 Unicode PARAGRAPH SEPARATOR

Wenn diese voll unterstützt werden, beeinflussen diese Zeilenendezeichen die Art, wie Textdateien eingelesen werden (bei Skriptsprachen außerdem die Art, wie der Interpreter die Datei einliest, die das Programm enthält). Bei regulären Ausdrücken wirkt sich das darauf aus, worauf der Punkt passt (siehe den Abschnitt Der Modus »Punkt passt auf alles«), und außerdem darauf, an welchen Positionen ˹^˼, ˹$˼ und ˹\Z˼ passen (siehe den Abschnitt Der Modus »Verbesserte Zeilenanker« oder Mehrzeilenmodus).

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Reguläre Ausdrücke bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:
   

Copyright der deutschen Ausgabe © 2008 by O’Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Reguläre Ausdrücke" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstr. 81, 50670 Köln