utf8 und Stringlängen

01/07/2010 - 19:05 von Peter Velan | Report spam
Hallo,

alle Welt schwàrmt vom Ende des Zeichenwirrwarrs weil doch UTF-8 jetzt
alles besser macht. Ich habe hier dennoch Probleme :-\

,-
#!/usr/bin/perl -w -s
#
use utf8;
use DBI;

my $host = 'localhost';
my $dbnam = 'xxx';
my $user = 'xxx';
my $pwd = 'xxx';
my $fld = 'meta_description';
my $tbl = 'www_pages';

my $dbh = DBI->connect
( "DBI:mysql:hostname=$host;database=$dbnam;mysql_enable_utf8=1"
, $user
, $pwd
, { RaiseError => 1, PrintError => 1, AutoCommit => 1 }
);

my $h0 = $dbh->prepare( "SELECT 'àüö' as v3" ); $h0->execute;
my $d0 = $h0->fetchrow_hashref();

my $v1 = '123';
my $v2 = 'àöü';
my $v3 = $d0->{v3};

print length( $v1 ), " $v1"; printf ' %-10s ', "'$v1'"; print "**";
print length( $v2 ), " $v2"; printf ' %-10s ', "'$v2'"; print "**";
print length( $v3 ), " $v3"; printf ' %-10s ', "'$v3'"; print "**";

exit;
`-

- Perl v5.10.1 auf Debian
- locale: "LANG=en_US.UTF-8"
- Skript in UTF8 verfasst [1]
- Zeilende 0A [1]
- kein BOM-Marker am Fileanfang [1]

Resultat:

,-
3 123 '123' **
3 ◙◙◙ '◙◙◙' **
6 àüö 'àüö' **
`-

1. Frage:

Warum die direkte Zuweisung > $v2 = 'àöü' < anders behandelt als der
aus der via DBI kommende Wert?

2. Frage:

Warum ist length($v3)=6, also keine Zeichen-, sondern eine Byte-Anzahl?


Was mache ich falsch?

Gruß,
Peter

[1] mit Hex-Editor überprüft
 

Lesen sie die antworten

#1 Peter J. Holzer
01/07/2010 - 23:46 | Warnen spam
On 2010-07-01 17:05, Peter Velan wrote:
alle Welt schwàrmt vom Ende des Zeichenwirrwarrs weil doch UTF-8 jetzt
alles besser macht. Ich habe hier dennoch Probleme :-\

,-
use utf8;


[...]
my $dbh = DBI->connect
( "DBI:mysql:hostname=$host;database=$dbnam;mysql_enable_utf8=1"
, $user
, $pwd
, { RaiseError => 1, PrintError => 1, AutoCommit => 1 }
);

my $h0 = $dbh->prepare( "SELECT 'àüö' as v3" ); $h0->execute;
my $d0 = $h0->fetchrow_hashref();

my $v1 = '123';
my $v2 = 'àöü';
my $v3 = $d0->{v3};

print length( $v1 ), " $v1"; printf ' %-10s ', "'$v1'"; print "**";
print length( $v2 ), " $v2"; printf ' %-10s ', "'$v2'"; print "**";
print length( $v3 ), " $v3"; printf ' %-10s ', "'$v3'"; print "**";

exit;
`-

- Perl v5.10.1 auf Debian
- locale: "LANG=en_US.UTF-8"
- Skript in UTF8 verfasst [1]
- Zeilende 0A [1]
- kein BOM-Marker am Fileanfang [1]

Resultat:

,-
3 123 '123' **
3 ◙◙◙ '◙◙◙' **



U+25D9? Wo kommt das her?

6 àüö 'àüö' **
`-

1. Frage:

Warum die direkte Zuweisung > $v2 = 'àöü' < anders behandelt als der
aus der via DBI kommende Wert?



Bei $v2 = 'àöü' erzeugt der Compiler einen konstanten String mit den
drei Zeichen 'àöü'. Dieser String wird direkt an $v2 zugewiesen. Das
Ergebnis sollte niemanden überraschen.

Bei

my $h0 = $dbh->prepare( "SELECT 'àüö' as v3" ); $h0->execute;
my $d0 = $h0->fetchrow_hashref();
my $v3 = $d0->{v3};



hingegen erzeugt der Compiler zuerst einmal einen Character-String mit
drei Umlauten. Dieser String wird mittels prepare an den mysql-Treiber
übergeben. Der übergibt ihn wieder an die mysql-Clientlibrary. Die weiß
nichts von Perl-Characterstrings, daher muss da (zumindest
konzeptionell) encode() aufgerufen werden. Die mysql-Client-Library
übergibt das Kommando an den Mysql-Server (wobei eventuell noch einmal
eine charset-Konversion durchgeführt wird). Der schickt ein Ergebnis
zurück (potentiell noch eine Konversion), die Client-Library übergibt
das an den Treiber, der macht Perl-Strings draus (wobei wohl ein
decode() angebracht wàre, und zuguterletzt wird das an $v3 zugewiesen.

Irgendwo in dieser Kette geht etwas schief. Meinem Verstàndnis nach
sollte mysql_enable_utf8 dafür sorgen, dass das alles automatisch
richtig funktioniert, aber die Option ist relativ neu (als ich mich
zuletzt mit UTF-8 und MySQL herumgeschlagen habe, war das alles noch
etwas komplizierter), möglicherweise bist Du auf einen Bug gestoßen.
Funktioniert es, wenn Du ein Select aus einer Tabelle machst?


2. Frage:

Warum ist length($v3)=6, also keine Zeichen-, sondern eine Byte-Anzahl?



Vermutlich, weil $v3 hier ein Bytestring ist, d.h. es fehlt das decode()
im Treiber, das den Bytestring, der vom Server kommt (merke: Übers Netz
und von Platte kommen immer Bytes, nie Zeichen) dekodieren sollte.

hp

Ähnliche fragen