(Update: eine überarbeitete und verbesserte Version dieses Artikels kann man hier finden.)
Wieso werden mit Perl meine Umlaut nicht richtig dargestellt? Wieso
funktioniert es auf einem Rechner, auf einem anderen nicht?
Grund ist meistens, dass der Programmier nicht genug darauf geachtet hat, wie
ein String codiert ist. Um das zu beheben, braucht man ein bisschen
Hintergrundwissen.
Am Anfang war alles ASCII
Als die Erde noch jung und elektronischer Speicherplatz Mangelware war,
überlegten sich clevere Leute, wie man ein ‘A’ binär kodieren könnte - und
einigte sich auf ASCII, den “American Standard Code for Information
Interchange”.
ASCII definiert 128 Zeichen, benutzt also 7 Bit. Das reicht für Klein- und
Großbuchstaben, Ziffern, Rechenzeichen, diverse Klammern und ein paar
Kontrollzeichen - Umlaute sind nicht dabei.
…dann ging das Chaos los
Zum Glück sind ein Byte 8 bit, d.h. man kann in einem Byte 256 Zeichen
kodieren - nur welche?
In den verschiedenen Ländern wurden, abhängig von der Sprache, verschiedene
Zeichen gebraucht. In Mitteleuropa z.B. für Deutschland die Umlaute und
scharfes s, für Frankreich akenzentuierte ‘e’s und “c cedile” ç, für
Großbritannien das Pfund-Zeichen £. Damit und mit ein paar anderen Zeichen
waren die restlichen 128 Zeichen schnell aufgebraucht. Den Zeichensatz für
Mitteleuropa nannte man “Latin1″ (Lateinische Zeichen, Sprachraum 1),
festgehalten im Standard iso-8859-1.
Aber in Griechenland, Russland und vielen anderen Ländern braucht man ganz
andere Zeichen, also sprossen die verschiedenen Kodierungen wie Unkraut aus
dem Boden: Latin2 für Osteuropa, Latin3 für Südeuropa, Latin4 für den
baltischen Sprachraum, Latin5 für Kyrillische Zeichen, Latin6 für arabische
und so weiter.
In den meisten Kodierungen sind die ersten 128 Zeichen mit ASCII identisch,
nur die zweiten 128 Zeichen unterscheiden sich (also die, die binär mit einer
1 anfangen).
Für die meisten asiatischen Schriften reichen 256 bei weitem nicht, der
Zeichensatz Big5 z.B. verwendet zwei Bytes pro chinesischem Schriftzeichen.
Das Problem
Das erste Problem daran ist, dass man mit diesen Zeichensätzen erst einmal nur
Zeichen aus einem Zeichensatz darstellen kann. Wer Umlaute verwendet, kann
keine griechischen Zeichen verwenden, keine arabischen, keine kyrillischen
usw.
Das zweite Problem ist, dass man von Daten wissen muss, in welcher Kodierung
sie vorlieben, um sie korrekt darzustellen. Z.B. bedeutet in Latin1 das Byte
0xE7 ‘à’, in Latin2 bedeutet es ‘ŕ’ und in Latin10 (nordisch) ist es ein ‘ā’.
Wenn man keine Informationen darüber hat, welche Interpretation man wählen
muss, kann man nur raten.
Unicode
Eine Lösung für das erste Problem ist Unicode. Das ist ein Projekt mit dem
Ziel, alle möglichen Zeichen zu kodieren - alle bekannten, zum Teil selbst
fiktionale Zeichen (z.B. klingonische Schrift).
Dabei wird jedem Zeichen (z.B. LATIN CAPITAL LETTER A) eine Zahl zugewiesen,
der sogenannte Codepoint. Der wird üblicherweise in hexadezimaler Notation
angegeben: U+0041.
Dabei gibt es nicht nur die “normalen” Zeichen, sondern auch die “combining
characters”, die andere Zeichen modifizieren - bekanntestes Beispiel sind
Akzente (ein A combiniert mit COMBINING GRAVE ACCENT wird dann zu À).
Damit ist aber noch nicht festgelegt, mit welchen Bytefolgen ein bestimmtes
Zeichen kodiert wird. Dazu gibt es die “Unicode Transformation Formats”, das
bekannteste ist UTF-8.
Die ersten 128 Zeichen von UTF-8 sind mit ASCII identisch, andere Zeichen
werden mit einer variablen Anzahl von Bytes kodiert, z.B. deutsche Umlaute mit
zwei Bytes, andere Zeichen können bis zu vier Bytes aufbrauchen.
Kodierungen und Perl
Perl kann ab Version 5.8 recht gut mit Unicode umgehen. Auch kann man zwischen
den verschiedenen Kodierungen relativ leicht umrechnen - man muss sich nur
bewußt sein, dass man es auch tun muss.
Perl verwendet ein internes Format, um Strings zu speichern. Man sollte alle
Eingabedaten in dieses Interne Format umwandeln bevor man etwas damit macht.
Dann werden Zeichen, die in mehreren Bytes kodiert werden, auch tatsächlich
als ein logisches Zeichen behandelt, wie das folgende Beispiel auf einem auf
UTF-8 eingestelltes Terminal zeigt:
$ echo 'ä' | perl -wlne 'chomp; print length($_)'
2
$ echo 'ä' | perl -MEncode -wlne 'chomp; $_ = decode("utf8", $_); print length($_);'
1
Hier wird das Core-Modul Encode geladen, und mit dessen
Funktion decode verwendet, um die Eingabedaten in perls internes
Format umzuwandeln. Dabei muss man die Kodierung der Eingabedaten angeben.
Wenn man diese nicht weiß, hat man ein großes Problem. Man kann probieren, mit
Heuristiken die Kodierung zu erraten, aber das ist unzuverlässig und
rechenintensiv. Wenn man es doch probieren will, kann man es so machen:
use Encode::Guess;
sub my_decode {
my $str = shift;
return '' if (!defined $str or $str eq qq{});
my @encodings = qw(ascii utf-8 iso-8859-15 gb2312);
my $encoder = guess_encoding($str, @encodings);
if (ref $encoder){
return $encoder->decode($str);
} else {
# irgend wie muss man die Daten dekodieren. Alternativ könnte man,
# je nach Anwendung, auch einen Fehler auslösen
return decode("latin1", $str);
}
}
# Hier Daten irgendwie in $input einlesen, und dann
my $internal = my_decode($input);
Wenn man die Daten ausgibt, muss man sie wieder in das Format kodieren, in dem
man sie ausgeben will. Das kann man z.B. per Hand machen:
use Encode;
print encode('utf8', $string);
Man kann auch Dateihandle dazu bringen, automatisch nach utf-8 zu
konvertieren:
open my $filehanlde, '>:utf8', 'dateiname.txt';
# oder wenn das filehandle schon offen ist:
binmode($filehandle, ':utf8');
binmode(STDOUT, ':utf8');
# oder mit anderen Kodierungen:
binmode($filehandle, ':encoding(iso-8859-1)');
Man kann auch dafür sorgen, dass alle Dateien zum lesen und schreiben in einem
ganzen Programm immer mit ‘:utf8′ geöffnet werden:
use open ':utf8';