8 Stimmen

Perl-Strings Interna

Wie werden Zeichenketten in Perl intern dargestellt? Welche Kodierung wird verwendet? Wie gehe ich mit verschiedenen Kodierungen richtig um?

Ich benutze Perl schon seit langer Zeit, aber es enthielt nicht viele Möglichkeiten zur Behandlung von Zeichenketten in verschiedenen Kodierungen, und wenn ich auf ein kleines Problem stieß, das etwas mit Kodierungen zu tun hatte, griff ich gewöhnlich auf einige schamanische Aktionen zurück.

Bis zu diesem Zeitpunkt dachte ich an Perl-Strings als Sequenzen von Bytes, was für meine Aufgaben ziemlich gut passte. Jetzt muss ich eine UTF-8 kodierte Datei verarbeiten und hier beginnen die Probleme.

Zuerst lese ich die Datei wie folgt in einen String ein:

open(my $in, '<', $ARGV[0]) or die "cannot open file $ARGV[0] for reading";
binmode($in, ':utf8');

my $contents;

{
    local $/;
    $contents = <$in>;
}

close($in);

dann drucken Sie es einfach aus:

print $contents;

Und ich bekomme zwei Dinge: eine Warnung Wide character in print at <scriptname> line <n> und eine Müllkonsole. Daraus kann ich schließen, dass Perl-Strings ein Konzept von "Zeichen" haben, das "breit" sein kann oder nicht, aber wenn sie gedruckt werden, werden diese "breiten" Zeichen in der Konsole als mehrere Bytes dargestellt, nicht als einzelnes "Zeichen". (Ich frage mich jetzt, warum alle meine früheren Erfahrungen mit Binärdateien so funktioniert haben, wie ich es erwartet hatte, ohne irgendwelche "Zeichen"-Probleme).

Warum sehe ich dann Müll in der Konsole? Wenn Perl Strings als Zeichen in einer bekannten Kodierung speichert, glaube ich nicht, dass es ein großes Problem ist, die Konsolenkodierung herauszufinden und den Text richtig zu drucken. (Ich benutze Windows, BTW).

Wenn Perl Zeichenketten als Zeichensequenzen mit variabler Breite speichert (z. B. unter Verwendung der gleichen UTF-8-Kodierung), warum geschieht das auf diese Weise? Nach meiner Erfahrung mit C ist der Umgang mit Zeichenketten eine Qual.

アップデート .

Ich verwende zwei Computer zum Testen, auf einem läuft Windows 7 x64 mit installiertem englischem Sprachpaket, aber mit russischen Regionaleinstellungen (ich habe also cp866 als OEM-Codepage und cp1251 als ANSI) mit ActivePerl 5.10.1 x64; auf dem anderen läuft Windows XP 32 Bit russische Lokalisierung mit Cygwin Perl 5.10.0.

Dank der Links habe ich jetzt ein viel besseres Verständnis dafür, was vor sich geht und wie die Dinge gehandhabt werden sollten.

4voto

dylan Punkte 64

Das Setzen von utf8 vor dem Lesen aus der Datei ist gut, da es die Bytes automatisch in die interne Kodierung dekodiert. (Die auch UTF-8 ist, aber das müssen Sie nicht wissen und sollten sich nicht darauf verlassen.)

Vor dem Drucken müssen Sie die Zeichen wieder in Bytes kodieren.

use Encode;  
utf8::encode($contents);

Es gibt auch eine Zwei-Argument-Form von encode, für andere Kodierungen als Unicode. (Dieser Satz hallt zu sehr nach, nicht wahr?)

Hier ist eine gute Referenz. (Wäre mehr gewesen, aber es ist mein erster Beitrag.) Schauen Sie sich auch perlunitut und den Unicode-Artikel auf Joel on Software.

http://www.ahinea.com/en/tech/perl-unicode-struggle.html

Oh, und es muss Multi-Byte-Strings verwenden, weil es sonst einfach kein Unicode ist.

4voto

Ven'Tatsu Punkte 3430

Perl-Strings werden intern in einer von zwei Kodierungen gespeichert, entweder in einer 8-Bit byteorientierten nativen Kodierung oder in UTF-8. Aus Gründen der Rückwärtsvergleichbarkeit wird davon ausgegangen, dass alle E/A und Strings in nativer Kodierung sind, sofern nicht anders angegeben. Die native Kodierung ist normalerweise 8-Bit-ASCII, aber das kann mit use locale .

In Ihrem Beispiel rufen Sie binmode auf Ihrem Eingabe-Handle auf und ändern es so, dass es :utf8 Semantik. Eine Auswirkung davon ist, dass alle Zeichenketten, die von diesem Handle gelesen werden, als UTF-8 kodiert werden. print schreibt an STDOUT standardmäßig, und STDOUT erwartet standardmäßig nativ kodierte Zeichen.

Perl versucht, das Richtige zu tun und lässt zu, dass eine UTF-8-Zeichenkette an eine nativ kodierte Ausgabe gesendet wird, aber wenn diesem Handle keine Kodierung zugeordnet ist, muss es raten, wie es Multibyte-Zeichen ausgeben soll, und es wird mit großer Wahrscheinlichkeit falsch raten. Die Warnung bedeutet, dass ein Multi-Byte-Zeichen an einen Stream gesendet wurde, der nur Ein-Byte-Zeichen erwartet, und das Ergebnis war, dass das Zeichen wahrscheinlich bei der Übersetzung beschädigt wurde.

Je nachdem, was Sie erreichen wollen, können Sie das von dylan erwähnte Encode-Modul verwenden, um die UTF-8-Daten in einen Ein-Byte-Zeichensatz zu konvertieren, der sicher gedruckt werden kann, oder wenn Sie wissen, dass das, was an STDOUT mit UTF-8 umgehen kann, können Sie binmode(STDOUT, ':utf8'); um Perl mitzuteilen, dass Sie alle Daten, die an STDOUT als UTF-8 gesendet werden.

2voto

weismat Punkte 6931

Sie sollten Ihre aktuellen Windows- und Perl-Versionen angeben, da dies wirklich von den verwendeten Versionen und installierten Sprachpaketen abhängt.
Ansonsten sehen Sie sich die PerlUnicode Handbuch zuerst -

Perl verwendet logisch breite Zeichen zur internen Darstellung von Zeichenketten.

wird sie Ihre Aussagen bestätigen.

Windows installiert nicht alle UTF8-Zeichen vollständig - dies könnte also der Grund für Ihr Problem sein. Möglicherweise müssen Sie ein zusätzliches Sprachpaket installieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X