11 Stimmen

Wie kann ich Perl dazu bringen, schlechte UTF-8-Sequenzen zu erkennen?

Ich verwende Perl 5.10.0 und Postgres 8.4.3, und Zeichenketten in eine Datenbank, die hinter einem DBIx::Klasse .

Diese Zeichenketten sollten in UTF-8 sein, und deshalb läuft meine Datenbank in UTF-8. Leider sind einige dieser Zeichenketten schlecht, da sie fehlerhaftes UTF-8 enthalten, so dass ich beim Ausführen eine Ausnahme erhalte

DBI Exception: DBD::Pg::st execute failed: ERROR: invalid byte sequence for encoding "UTF8": 0xb5

Ich dachte, ich könnte die ungültigen einfach ignorieren und mich später um die fehlerhaften UTF-8-Titel kümmern.

if(not utf8::valid($title)){
   $title="Invalid UTF-8";
}
$data->title($title);
$data->update();

Perl scheint jedoch davon auszugehen, dass die Zeichenketten gültig sind, löst aber trotzdem die Ausnahmen aus.

Wie kann ich Perl dazu bringen, das schlechte UTF-8 zu erkennen?

9voto

rjh Punkte 47430

Bitte beachten Sie zunächst die Dokumentation - die utf8 Modul sollte sólo in der Form 'use utf8;' verwendet werden, um anzugeben, dass Ihr Quellcode UTF-8 anstelle von Latin-1 ist. Verwenden Sie keine der utf8-Funktionen.

Perl unterscheidet zwischen Bytes und UTF-8-Strings. Im Bytemodus weiß Perl nicht, welche Kodierung Sie verwenden, und wird Latin-1 verwenden, wenn Sie es ausgeben. Nehmen Sie zum Beispiel das Euro-Zeichen (€). In UTF-8 sind dies 3 Bytes, 0xE2, 0x82, 0xAC. Wenn Sie die Länge dieser Bytes ausgeben, wird Perl 3 zurückgeben. Auch hier spielt die Kodierung keine Rolle. Es kann jedes Byte oder jede Kodierung sein, legal oder illegal.

Wenn Sie die Encode Modul und rufen Encode::decode("UTF-8', $bytes) erhalten Sie eine neue Zeichenkette, bei der das sogenannte UTF8-Flag gesetzt ist. Perl weiß nun, dass Ihre Zeichenkette in UTF-8 vorliegt, und gibt die Länge 1 zurück.

Das Problem, das utf8::valid gilt nur für die zweite Art von Zeichenfolge. Ihre Zeichenketten sind wahrscheinlich in der ersten Form, dem Byte-Modus, und utf8::valid gibt einfach true für alles in Byte-Form zurück. Dies ist in der perldoc dokumentiert.

Die Lösung besteht darin, Perl dazu zu bringen, Ihre Byte-Strings als UTF-8 zu dekodieren und alle Fehler zu erkennen. Dies kann mit FB_CROAK gemacht werden, wie brian d foy erklärt:

my $ustring =
    eval { decode( 'UTF-8', $byte_string, FB_CROAK ) }
    or die "Could not decode string: $@";

Sie können dann diesen Fehler abfangen und die ungültigen Zeichenfolgen überspringen.

Oder wenn Sie wissen, dass Ihr Code größtenteils UTF-8 ist, mit ein paar ungültigen Sequenzen hier und da, können Sie verwenden:

my $ustring = decode( 'UTF-8', $byte_string );

die den Standardmodus von FB_DEFAULT und ersetzt ungültige Zeichen durch U+FFFD, das Unicode-Ersetzungszeichen (Raute mit Fragezeichen).

In den meisten Fällen können Sie die Zeichenfolge dann direkt an Ihren Datenbanktreiber übergeben. Bei einigen Treibern müssen Sie die Zeichenkette zunächst wieder in die Byteform zurückkodieren:

my $byte_string = encode('UTF-8', $ustring);

Es gibt auch Regexe online, die Sie verwenden können, um auf gültige UTF-8-Sequenzen zu prüfen, bevor Sie decode (siehe andere Stack Overflow-Antworten). Wenn Sie diese Regexe verwenden, müssen Sie keine Kodierung oder Dekodierung vornehmen.

Schließlich verwenden Sie bitte UTF-8 statt utf8 in Ihren Aufrufen an decode . Letztere ist laxer und lässt einige ungültige UTF-8-Sequenzen (wie Sequenzen außerhalb des Unicode-Bereichs) durch.

9voto

brian d foy Punkte 124323

Wie erhalten Sie Ihre Saiten? Sind Sie sicher, dass Perl denkt, dass sie bereits UTF-8 sind? Wenn sie noch nicht dekodiert sind (d.h. die Oktette werden als eine Kodierung interpretiert), müssen Sie das selbst tun:

    use Encode;

    my $ustring =
      eval { decode( 'utf8', $byte_string, FB_CROAK ) }
      or die "Could not decode string: $@";

Besser noch: Wenn Sie wissen, dass Ihre Zeichenkettenquelle bereits UTF-8 ist, müssen Sie diese Quelle als UTF-8 lesen. Schauen Sie sich den Code an, mit dem Sie die Zeichenketten abrufen, um zu sehen, ob Sie das richtig machen.

2voto

hobbs Punkte 204816

Da die Dokumentation für utf8::valid weist darauf hin, dass er true zurückgibt, wenn die Zeichenkette als UTF-8 markiert ist und es sich um gültiges UTF-8 handelt, oder wenn die Zeichenkette gar nicht UTF-8 ist . Obwohl es unmöglich ist zu sagen, ohne den Code im Kontext zu sehen und zu wissen, was die Daten sind, ist das, was Sie wollen, höchstwahrscheinlich nicht die "gültige utf8"-Prüfung überhaupt; wahrscheinlich müssen Sie nur tun

$data->title( Encode::encode("UTF-8", $title) )

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