Die Verarbeitung von Unicode-Text erfolgt in zwei Schritten. Das erste ist "Wie kann ich es eingeben und ausgeben, ohne Informationen zu verlieren". Die zweite lautet: "Wie behandle ich Text gemäß den Konventionen der Landessprache?"
Der Beitrag von tchrist deckt beide ab, aber im zweiten Teil stammen 99% des Textes in seinem Beitrag. Die meisten Programme verarbeiten E / A nicht einmal richtig, daher ist es wichtig zu verstehen, bevor Sie sich überhaupt Gedanken über Normalisierung und Sortierung machen.
Dieser Beitrag zielt darauf ab, dieses erste Problem zu lösen
Wenn Sie Daten in Perl einlesen, ist es egal, um welche Codierung es sich handelt. Es reserviert etwas Speicher und versteckt die Bytes dort. Wenn Sie sagen print $str
, werden diese Bytes nur an Ihr Terminal ausgegeben, was wahrscheinlich davon ausgeht, dass alles, was darauf geschrieben wird, UTF-8 ist, und Ihr Text wird angezeigt.
Wunderbar.
Außer es ist nicht. Wenn Sie versuchen, die Daten als Text zu behandeln, werden Sie feststellen, dass etwas Schlimmes passiert. Sie müssen nicht weiter gehen, als length
zu sehen, dass das, was Perl über Ihre Saite denkt und was Sie über Ihre Saite denken, nicht übereinstimmt. Schreiben Sie einen Einzeiler wie: perl -E 'while(<>){ chomp; say length }'
und geben Sie ein文字化け
und Sie erhalten 12 ... nicht die richtige Antwort, 4.
Das liegt daran, dass Perl davon ausgeht, dass Ihre Zeichenfolge kein Text ist. Sie müssen ihm sagen, dass es sich um Text handelt, bevor Sie die richtige Antwort erhalten.
Das ist einfach genug; Das Encode-Modul verfügt über die entsprechenden Funktionen. Der generische Einstiegspunkt ist Encode::decode
(oderuse Encode qw(decode)
natürlich). Diese Funktion nimmt eine Zeichenfolge aus der Außenwelt (was wir "Oktette" nennen, eine ausgefallene Art, "8-Bit-Bytes" zu sagen) und wandelt sie in einen Text um, den Perl verstehen wird. Das erste Argument ist ein Zeichencodierungsname wie "UTF-8" oder "ASCII" oder "EUC-JP". Das zweite Argument ist die Zeichenfolge. Der Rückgabewert ist der Perl-Skalar, der den Text enthält.
(Es gibt auch Encode::decode_utf8
UTF-8 für die Codierung.)
Wenn wir unseren Einzeiler umschreiben:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Wir geben 文字 化 け ein und erhalten als Ergebnis "4". Erfolg.
Genau dort ist die Lösung für 99% der Unicode-Probleme in Perl.
Der Schlüssel ist, wann immer Text in Ihr Programm kommt, müssen Sie ihn dekodieren. Das Internet kann keine Zeichen übertragen. Dateien können keine Zeichen speichern. Ihre Datenbank enthält keine Zeichen. Es gibt nur Oktette, und Sie können Oktette in Perl nicht als Zeichen behandeln. Sie müssen die codierten Oktette mit dem Encode-Modul in Perl-Zeichen dekodieren.
Die andere Hälfte des Problems besteht darin, Daten aus Ihrem Programm abzurufen. Das ist einfach; Sie sagen einfach use Encode qw(encode)
, entscheiden Sie , in welcher Codierung Ihre Daten enthalten sein sollen (UTF-8 für Terminals, die UTF-8, UTF-16 für Windows-Dateien usw. verstehen), und geben dann das Ergebnis aus, encode($encoding, $data)
anstatt nur auszugeben $data
.
Diese Operation konvertiert Perls Zeichen, mit denen Ihr Programm arbeitet, in Oktette, die von der Außenwelt verwendet werden können. Es wäre viel einfacher, wenn wir nur Zeichen über das Internet oder an unsere Terminals senden könnten, aber wir können nicht: nur Oktette. Wir müssen also Zeichen in Oktette konvertieren, sonst sind die Ergebnisse undefiniert.
Zusammenfassend: Kodieren Sie alle Ausgänge und dekodieren Sie alle Eingänge.
Jetzt werden wir über drei Themen sprechen, die dies ein wenig herausfordernd machen. Das erste sind Bibliotheken. Behandeln sie Text richtig? Die Antwort ist ... sie versuchen es. Wenn Sie eine Webseite herunterladen, erhalten Sie von LWP Ihr Ergebnis als Text zurück. Wenn Sie die richtige Methode für das Ergebnis aufrufen, ist dies (und das ist zufällig decoded_content
nicht content
der Oktett-Stream, den es vom Server erhalten hat). Datenbanktreiber können unzuverlässig sein. Wenn Sie DBD :: SQLite nur mit Perl verwenden, wird es funktionieren, aber wenn ein anderes Tool Text als andere Codierung als UTF-8 in Ihre Datenbank aufgenommen hat ... nun ... wird es nicht richtig gehandhabt bis Sie Code schreiben, um damit richtig umzugehen.
Die Ausgabe von Daten ist normalerweise einfacher, aber wenn Sie "breites Zeichen im Druck" sehen, wissen Sie, dass Sie die Codierung irgendwo durcheinander bringen. Diese Warnung bedeutet "Hey, du versuchst Perl-Charaktere nach außen zu lecken und das macht keinen Sinn". Ihr Programm scheint zu funktionieren (weil das andere Ende normalerweise die rohen Perl-Zeichen korrekt verarbeitet), aber es ist sehr kaputt und kann jeden Moment nicht mehr funktionieren. Beheben Sie es mit einem expliziten Encode::encode
!
Das zweite Problem ist UTF-8-codierter Quellcode. Wenn Sie nicht use utf8
oben in jeder Datei sagen , geht Perl nicht davon aus, dass Ihr Quellcode UTF-8 ist. Dies bedeutet, dass Sie jedes Mal, wenn Sie so etwas sagen my $var = 'ほげ'
, Müll in Ihr Programm injizieren, der alles schrecklich kaputt macht. Sie müssen nicht auf „Verwendung utf8“, aber wenn Sie das nicht tun, Sie müssen keine Nicht-ASCII - Zeichen in Ihrem Programm verwenden.
Das dritte Problem ist, wie Perl mit The Past umgeht. Vor langer Zeit gab es kein Unicode, und Perl nahm an, dass alles Latin-1-Text oder Binär war. Wenn also Daten in Ihr Programm eingehen und Sie beginnen, sie als Text zu behandeln, behandelt Perl jedes Oktett als Latin-1-Zeichen. Deshalb haben wir, als wir nach der Länge von "文字 化 け" gefragt haben, 12 bekommen. Perl nahm an, dass wir mit der Latin-1-Zeichenfolge "æååã" arbeiten (12 Zeichen, von denen einige nicht gedruckt werden).
Dies wird als "implizites Upgrade" bezeichnet und ist durchaus sinnvoll, aber es ist nicht das, was Sie wollen, wenn Ihr Text nicht Latin-1 ist. Aus diesem Grund ist es wichtig, Eingaben explizit zu dekodieren: Wenn Sie dies nicht tun, wird Perl dies tun, und es kann sein, dass es falsch ist.
Menschen geraten in Schwierigkeiten, wenn die Hälfte ihrer Daten eine richtige Zeichenfolge ist und einige immer noch binär. Perl interpretiert den Teil, der noch binär ist, als wäre es Latin-1-Text, und kombiniert ihn dann mit den richtigen Zeichendaten. Dadurch sieht es so aus, als ob der richtige Umgang mit Ihren Charakteren Ihr Programm kaputt gemacht hat, aber in Wirklichkeit haben Sie es einfach nicht genug behoben.
Hier ein Beispiel: Sie haben ein Programm, das eine UTF-8-codierte Textdatei liest, Sie kleben einen Unicode PILE OF POO
an jede Zeile und drucken ihn aus. Du schreibst es wie:
while(<>){
chomp;
say "$_ 💩";
}
Führen Sie dann einige UTF-8-codierte Daten aus, z.
perl poo.pl input-data.txt
Es druckt die UTF-8-Daten mit einem Poo am Ende jeder Zeile. Perfekt, mein Programm funktioniert!
Aber nein, Sie machen nur binäre Verkettung. Sie lesen Oktette aus der Datei, entfernen ein \n
mit chomp und heften dann die Bytes in der UTF-8-Darstellung des PILE OF POO
Zeichens an. Wenn Sie Ihr Programm überarbeiten, um die Daten aus der Datei zu dekodieren und die Ausgabe zu kodieren, werden Sie feststellen, dass Sie Müll ("ð ©") anstelle von poo erhalten. Dies lässt Sie glauben, dass das Dekodieren der Eingabedatei falsch ist. Es ist nicht.
Das Problem ist, dass der Poo implizit als Latin-1 aktualisiert wird. Wenn Sie use utf8
den wörtlichen Text anstelle des Binärtextes erstellen, funktioniert er wieder!
(Das ist das Hauptproblem, das ich sehe, wenn ich Menschen mit Unicode helfe. Sie haben sich richtig getrennt und das hat ihr Programm gebrochen. Das ist das Traurige an undefinierten Ergebnissen: Sie können ein funktionierendes Programm für eine lange Zeit haben, aber wenn Sie anfangen, es zu reparieren, Keine Sorge, wenn Sie Ihrem Programm Codierungs- / Dekodierungsanweisungen hinzufügen und es kaputt geht, bedeutet dies nur, dass Sie mehr Arbeit zu erledigen haben. Wenn Sie das nächste Mal von Anfang an mit Unicode arbeiten, wird dies der Fall sein viel einfacher!)
Das ist wirklich alles, was Sie über Perl und Unicode wissen müssen. Wenn Sie Perl mitteilen, was Ihre Daten sind, bietet es die beste Unicode-Unterstützung unter allen gängigen Programmiersprachen. Wenn Sie davon ausgehen, dass es auf magische Weise weiß, welche Art von Text Sie ihm zuführen, werden Sie Ihre Daten unwiderruflich in den Papierkorb werfen. Nur weil Ihr Programm heute auf Ihrem UTF-8-Terminal funktioniert, heißt das nicht, dass es morgen auf einer UTF-16-codierten Datei funktioniert. Machen Sie es jetzt sicher und ersparen Sie sich die Kopfschmerzen, die Daten Ihrer Benutzer zu zerstören!
Der einfache Teil der Handhabung von Unicode ist das Codieren der Ausgabe und das Decodieren der Eingabe. Der schwierige Teil besteht darin, alle Ihre Ein- und Ausgaben zu finden und zu bestimmen, um welche Codierung es sich handelt. Aber deshalb bekommst du das große Geld :)