URI-unescape innerhalb utf8 - Encoding Problem
URI-unescape innerhalb utf8 - Encoding Problem
am 14.09.2006 10:05:24 von Helmut Wollmersdorfer
Hallo,
was für Encoding-Spezialisten.
Ich möchte einen Text parsen, der Strings in folgenden Encodings enthält:
- UTF-8
- XML-escaped HTML-Entities (also z.B. 'ü')
- URI-escaped UTF-8 (z.B. '%E3%82%AF%E3%82%B8%E3%83%A9%E7%9B%AE')
Ein kleines Testprogramm hat funktioniert:
#!/usr/bin/perl -w
use strict;
use URI::Escape;
#use encoding 'utf-8'; # does not work
my $content = '%E3%82%AF%E3%82%B8%E3%83%A9%E7%9B%AEÃÃÃ';
#utf8::upgrade($content); # not needed
#$content =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack("C",hex($1))/ge;
$content =~ s/%([\dA-Fa-f][\dA-Fa-f])/chr(hex($1))/ge;
my $decoded = $content;
#my $decoded = uri_unescape ($content); # works
#utf8::upgrade($decoded); # does not work
utf8::decode($decoded);
my $utf8 = $decoded;
binmode(STDOUT, ":utf8"); # needed to avoid warning
print "$utf8\n";
Nur sieht das in meiner Anwendung dann so aus:
#!/usr/bin/perl -w
use strict;
use Cwd;
use HTML::Entities;
use Encode::Byte;
my $COMMON_MATCH
= '[\p{Alphabetic}\'´`][\p{Alphabetic} \'´`,-]*[\p{Alphabetic}\'´`]';
my $ENCODING = 'utf8';
open (XML, "<:encoding($ENCODING)", "$DIR/$file")
or die "Can't open: $1!";
CONTENT: while (my $data = ) {
my $content = decode_entities (decode_entities($data));
$content =~ s/%([\dA-Fa-f][\dA-Fa-f])/chr(hex($1))/ge;
utf8::decode($content);
my @lines = split ( /\n/,$content);
binmode(STDOUT, ":utf8");
LINE: for my $line ( @lines ) {
if ($line =~ m|^\P{Alphabetic}*($COMMON_MATCH)|) {
my $nam = $1;
print "$nam\n";
}
}
}
Was nicht funktioniert.
Wenn ich das URI-unescape verlagere
LINE: for my $line ( @lines ) {
if ( ... ) {
...
}
elsif ($line =~ m|^\P{Alphabetic}*((%[\dA-Fa-f][\dA-Fa-f])+)|) {
my $nam = $1;
$nam =~ s/%([\dA-Fa-f][\dA-Fa-f])/chr(hex($1))/ge;
utf8::decode($nam);
print "$title|$nam|$language|$date|$SITE/$DIR/$file\n";
}
}
dann klappt es. Ich würde aber gern zuerst alles in ein einheitliches
Encoding konvertieren, und dann darin parsen. Dass nämlich ganze Worte
URL-escaped sind, ist hier Zufall.
Anscheinend gibt es in der Behandlung eines Strings Unterschiede, je
nachdem ob er
- nur Zeichen der Latin-1 Menge enthält, oder
- auch Zeichen ausserhalb Latin-1
Leider sind die entsprechenden 'perldoc' wie perlunicode, utf8 etc.
geschrieben wie das 'Geschwätz alter Damen', sodass ich daraus nicht
wirklich schlau werde.
Gibt es irgendwo eine exakte Dokumentation im Stil von 'IF ... THEN ...'
mit exakten Bezeichnungen etc., oder kann mir hier jemand erklären, wie
man das generell und stabil lösen kann?
BTW: Perl 5.8.8
TIA
Helmut Wollmersdorfer
Re: URI-unescape innerhalb utf8 - Encoding Problem
am 14.09.2006 22:20:07 von Slaven Rezic
Helmut Wollmersdorfer writes:
> Hallo,
>
> was für Encoding-Spezialisten.
>
> Ich möchte einen Text parsen, der Strings in folgenden Encodings enthält:
> - UTF-8
> - XML-escaped HTML-Entities (also z.B. 'ü')
> - URI-escaped UTF-8 (z.B. '%E3%82%AF%E3%82%B8%E3%83%A9%E7%9B%AE')
>
Ich hoffe, du benutzt ein modernes Perl. Auf jeden Fall nichts aus der
5.6er-Reihe und auch nicht 5.8.0.
> Ein kleines Testprogramm hat funktioniert:
>
> #!/usr/bin/perl -w
> use strict;
> use URI::Escape;
> #use encoding 'utf-8'; # does not work
>
> my $content = '%E3%82%AF%E3%82%B8%E3%83%A9%E7%9B%AEÄÜÖ';
Du hast das Skript nicht mit "use utf8", d.h. die Umlaute hier liegen
in iso-8859-1 vor? Und in welchem Encoding waren die escapten Zeichen?
Ist das tatsächlich utf-8 gewesen?
> #utf8::upgrade($content); # not needed
>
> #$content =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack("C",hex($1))/ge;
> $content =~ s/%([\dA-Fa-f][\dA-Fa-f])/chr(hex($1))/ge;
> my $decoded = $content;
> #my $decoded = uri_unescape ($content); # works
Warum verwendest du es nicht?
>
> #utf8::upgrade($decoded); # does not work
> utf8::decode($decoded);
Ich bevorzuge Encode zum Konvertieren (die utf-8-Manpage auch).
> my $utf8 = $decoded;
>
> binmode(STDOUT, ":utf8"); # needed to avoid warning
Diese Warnung zeichnet ein Prinzip bei der Behandlung von utf-8 (und
anderen Encodings) bei der Ein- und Ausgabe: es ist die Aufgabe des
Programmierers, explizit zu sagen, in welchem Encoding Zeichen aus der
Außenwelt vorliegen und in welchem Encoding Zeichen ausgegeben werden
sollen.
> print "$utf8\n";
>
> Nur sieht das in meiner Anwendung dann so aus:
>
> #!/usr/bin/perl -w
> use strict;
>
> use Cwd;
> use HTML::Entities;
> use Encode::Byte;
>
> my $COMMON_MATCH
> = '[\p{Alphabetic}\'´`][\p{Alphabetic} \'´`,-]*[\p{Alphabetic}\'´`]';
>
> my $ENCODING = 'utf8';
>
> open (XML, "<:encoding($ENCODING)", "$DIR/$file")
> or die "Can't open: $1!";
>
> CONTENT: while (my $data = ) {
> my $content = decode_entities (decode_entities($data));
> $content =~ s/%([\dA-Fa-f][\dA-Fa-f])/chr(hex($1))/ge;
> utf8::decode($content);
Das reicht nicht aus. Wenn man Regular Expressions mit
Unicode-Properties verwendet, sollte man zusichern, dass alle
verwendeten Strings das utf8-Flag bekommen (ansonsten kommt Perl mit
Strings mit und ohne utf8-Flag zurecht). D.h. du solltest noch ein
utf8::upgrade verwenden. Übrigens kann man mit Dump() aus Devel::Peek
gut erkennen, ob ein String das Flag hat oder nicht.
>
> my @lines = split ( /\n/,$content);
> binmode(STDOUT, ":utf8");
>
> LINE: for my $line ( @lines ) {
> if ($line =~ m|^\P{Alphabetic}*($COMMON_MATCH)|) {
> my $nam = $1;
> print "$nam\n";
> }
> }
> }
>
> Was nicht funktioniert.
>
> Wenn ich das URI-unescape verlagere
>
> LINE: for my $line ( @lines ) {
> if ( ... ) {
> ...
> }
> elsif ($line =~ m|^\P{Alphabetic}*((%[\dA-Fa-f][\dA-Fa-f])+)|) {
>
> my $nam = $1;
> $nam =~ s/%([\dA-Fa-f][\dA-Fa-f])/chr(hex($1))/ge;
> utf8::decode($nam);
> print "$title|$nam|$language|$date|$SITE/$DIR/$file\n";
> }
> }
>
> dann klappt es. Ich würde aber gern zuerst alles in ein einheitliches
> Encoding konvertieren, und dann darin parsen. Dass nämlich ganze Worte
> URL-escaped sind, ist hier Zufall.
>
> Anscheinend gibt es in der Behandlung eines Strings Unterschiede, je
> nachdem ob er
> - nur Zeichen der Latin-1 Menge enthält, oder
> - auch Zeichen ausserhalb Latin-1
Genau. Wenn schon Zeichen außerhalb latin1 enthalten sind, dann *muss*
Perl intern einen utf-8-String bauen.
> Leider sind die entsprechenden 'perldoc' wie perlunicode, utf8 etc.
> geschrieben wie das 'Geschwätz alter Damen', sodass ich daraus nicht
> wirklich schlau werde.
>
> Gibt es irgendwo eine exakte Dokumentation im Stil von 'IF ... THEN
> ...' mit exakten Bezeichnungen etc., oder kann mir hier jemand
> erklären, wie man das generell und stabil lösen kann?
>
> BTW: Perl 5.8.8
Ach so. Vergiss die Frage oben.
Gruß,
Slaven
--
Slaven Rezic - slaven rezic de
babybike - routeplanner for cyclists in Berlin
handheld (e.g. Compaq iPAQ with Linux) version of bbbike
http://bbbike.sourceforge.net
Re: URI-unescape innerhalb utf8 - Encoding Problem
am 15.09.2006 08:15:00 von hjp-usenet2
On 2006-09-14 08:05, Helmut Wollmersdorfer wrote:
> Hallo,
>
> was für Encoding-Spezialisten.
>
> Ich möchte einen Text parsen, der Strings in folgenden Encodings enthält:
> - UTF-8
> - XML-escaped HTML-Entities (also z.B. 'ü')
> - URI-escaped UTF-8 (z.B. '%E3%82%AF%E3%82%B8%E3%83%A9%E7%9B%AE')
>
> Ein kleines Testprogramm hat funktioniert:
>
> #!/usr/bin/perl -w
> use strict;
> use URI::Escape;
> #use encoding 'utf-8'; # does not work
>
> my $content = '%E3%82%AF%E3%82%B8%E3%83%A9%E7%9B%AEÃÃÃ';
Hast Du das File als UTF-8 abgespeichert, d.h. sind Ã, Ã, Ã jeweils zwei
Bytes? Das sieht man dem Usenet-Posting nicht an, daher wäre es für die
Nachvollziehbarkeit von Vorteil, wenn Du das entweder dazuschreibst,
oder die einzelnen Bytes explizit hexadezimal hinschreibst:
my $content = "%E3%82%AF%E3%82%B8%E3%83%A9%E7%9B%AE\x{c3}\x{84}\x{c3}\x{9c }\x{c3}\x{96}";
> my $ENCODING = 'utf8';
>
> open (XML, "<:encoding($ENCODING)", "$DIR/$file")
> or die "Can't open: $1!";
Du führst hier beim Lesen des Files bereits eine Konversion von UTF-8 in
die perl-interne Wide-Character-Kodierung durch.
> CONTENT: while (my $data = ) {
> my $content = decode_entities (decode_entities($data));
> $content =~ s/%([\dA-Fa-f][\dA-Fa-f])/chr(hex($1))/ge;
> utf8::decode($content);
Und hier dekodierst Du noch einmal. Das ist einmal zu oft.
Da die Konversion erst nach dem dekodieren des URI-Encodings passieren
soll, solltest Du das File wahrscheinlich mit ':raw' öffnen.
decode_entities muss dann halt UTF-8 liefern.
> Was nicht funktioniert.
Ein Hinweis darauf, *wie* es nicht funktioniert, wäre hilfreich.
> Anscheinend gibt es in der Behandlung eines Strings Unterschiede, je
> nachdem ob er
> - nur Zeichen der Latin-1 Menge enthält, oder
> - auch Zeichen ausserhalb Latin-1
Jein. Der Unterschied ist, ob der String das utf8-Flag gesetzt hat. Wenn
er es hat (was immer der Fall ist, wenn der String Zeichen > 0xFF
enthält, aber auch bei Zeichen <= 0xFF der Fall sein kann), dann nimmt
Perl an, dass er Unicode-Zeichen enthält - \x{C4} ("Ã") ist also ein
Buchstabe. Wenn das Flag nicht gesetzt ist, dann nimmt Perl an, dass es
sich um ASCII bzw. ein nicht näher definiertes Superset davon handelt -
\x{C4} ist also kein Buchstabe, weil nicht definiert (man kann es aber
durch Verwendung einer geeigneten Locale definieren).
hp
--
_ | Peter J. Holzer | > Wieso sollte man etwas erfinden was nicht
|_|_) | Sysadmin WSR | > ist?
| | | hjp@hjp.at | Was sonst wäre der Sinn des Erfindens?
__/ | http://www.hjp.at/ | -- P. Einstein u. V. Gringmuth in desd