2010-12-02 42 views
7

J'ai des résultats intéressants en essayant de discerner les différences entre l'utilisation de Encode::decode("utf8", $var) et utf8::decode($var). J'ai déjà découvert qu'appeler l'ancien plusieurs fois sur une variable finira par entraîner une erreur "Impossible de décoder la chaîne avec des caractères larges à ..." alors que la dernière méthode fonctionnera heureusement autant de fois que vous le souhaitez, renvoyant simplement faux. Ce que j'ai de la difficulté à comprendre, c'est comment la fonction length renvoie des résultats différents selon la méthode que vous utilisez pour décoder. Le problème se pose parce que j'ai affaire à du texte utf8 "doublement encodé" provenant d'un fichier extérieur. Pour illustrer ce problème, j'ai créé un fichier texte "test.txt" avec les caractères Unicode suivants sur une ligne: U + 00e8, U + 00ab, U + 0086, U + 000a. Ces caractères Unicode sont le double-encodage du caractère Unicode U + 8acb, avec un caractère de nouvelle ligne. Le fichier a été codé sur disque en UTF8. Je lance le script Perl suivant:Perl: utf8 :: decode contre Encode :: decode

#!/usr/bin/perl                                   
use strict; 
use warnings; 
require "Encode.pm"; 
require "utf8.pm"; 

open FILE, "test.txt" or die $!; 
my @lines = <FILE>; 
my $test = $lines[0]; 

print "Length: " . (length $test) . "\n"; 
print "utf8 flag: " . utf8::is_utf8($test) . "\n"; 
my @unicode = (unpack('U*', $test)); 
print "Unicode:\[email protected]\n"; 
my @hex = (unpack('H*', $test)); 
print "Hex:\[email protected]\n"; 

print "==============\n"; 

$test = Encode::decode("utf8", $test); 
print "Length: " . (length $test) . "\n"; 
print "utf8 flag: " . utf8::is_utf8($test) . "\n"; 
@unicode = (unpack('U*', $test)); 
print "Unicode:\[email protected]\n"; 
@hex = (unpack('H*', $test)); 
print "Hex:\[email protected]\n"; 

print "==============\n"; 

$test = Encode::decode("utf8", $test); 
print "Length: " . (length $test) . "\n"; 
print "utf8 flag: " . utf8::is_utf8($test) . "\n"; 
@unicode = (unpack('U*', $test)); 
print "Unicode:\[email protected]\n"; 
@hex = (unpack('H*', $test)); 

print "Hex:\[email protected]\n"; 

Cela donne le résultat suivant:

Length: 7 
utf8 flag: 
Unicode: 
195 168 194 171 194 139 10 
Hex: 
c3a8c2abc28b0a 
============== 
Length: 4 
utf8 flag: 1 
Unicode: 
232 171 139 10 
Hex: 
c3a8c2abc28b0a 
============== 
Length: 2 
utf8 flag: 1 
Unicode: 
35531 10 
Hex: 
e8ab8b0a 

C'est ce que j'attendais. La longueur est à l'origine 7 parce que perl pense que $ test est juste une série d'octets. Après le décodage une fois, perl sait que $ test est une série de caractères encodés en utf8 (c'est-à-dire au lieu de retourner une longueur de 7 octets, perl renvoie une longueur de 4 caractères, même si $ test est encore 7 octets en mémoire). Après le deuxième décodage, $ test contient 4 octets interprétés comme 2 caractères, ce que j'attendrais puisque Encode :: decode prend les 4 points de code et les interprète comme des octets encodés en utf8, ce qui donne 2 caractères. La chose étrange est quand je modifie le code pour appeler utf8 :: decode à la place (remplacer tous $ test = Encode :: decode ("utf8", $ test), avec utf8 :: decode ($ test))

donne une sortie presque identique, seul le résultat d'une longueur différente:

 
Length: 7 
utf8 flag: 
Unicode: 
195 168 194 171 194 139 10 
Hex: 
c3a8c2abc28b0a 
============== 
Length: 4 
utf8 flag: 1 
Unicode: 
232 171 139 10 
Hex: 
c3a8c2abc28b0a 
============== 
Length: 4 
utf8 flag: 1 
Unicode: 
35531 10 
Hex: 
e8ab8b0a 

Il semble que perl premier compte les octets avant le décodage (comme prévu), puis compte les caractères après le premier décodage, mais compte les octets à nouveau après le deuxième décodage (pas prévu). Pourquoi ce changement aurait-il lieu? Y a-t-il une défaillance dans ma compréhension du fonctionnement de ces fonctions de décodage?

Merci,
Matt

+1

Pourquoi avez-vous besoin de modules au lieu de les utiliser? –

+1

Je n'ai pas use utf8 car cela indique à perl que votre code est lui-même encodé en utf8, ce dont je n'ai pas besoin (http://perldoc.perl.org/utf8.html). Je suppose que j'aurais pu encoder, mais je ne suis pas arrivé. – Matt

Répondre

3

Vous n'êtes pas censés utiliser les fonctions du module utf8 pragma. Its documentation dit alors:

Ne pas utiliser ce pragma pour autre chose que de dire que votre Perl script est écrit en UTF-8.

Always use the Encode module, et aussi voir la question Checklist for going the Unicode way with Perl. unpack est trop bas niveau, il ne vous donne même pas de vérification d'erreur.

Vous allez mal avec l'hypothèse que les octects E8 AB 86 0A sont le résultat de UTF-8 double encodage les caractères et newline. Ceci est la représentation d'un codage unique UTF-8 de ces caractères. Peut-être que toute la confusion de votre côté vient de cette erreur.

length est indûment surchargé, à certains moments, il détermine la longueur en caractères, ou la longueur en octets. Utilisez de meilleurs outils tels que Devel::Peek.

#!/usr/bin/env perl 
use strict; 
use warnings FATAL => 'all'; 
use Devel::Peek qw(Dump); 
use Encode qw(decode); 

my $test = "\x{00e8}\x{00ab}\x{0086}\x{000a}"; 
# or read the octets without implicit decoding from a file, does not matter 

Dump $test; 
# FLAGS = (PADMY,POK,pPOK) 
# PV = 0x8d8520 "\350\253\206\n"\0 

$test = decode('UTF-8', $test, Encode::FB_CROAK); 
Dump $test; 
# FLAGS = (PADMY,POK,pPOK,UTF8) 
# PV = 0xc02850 "\350\253\206\n"\0 [UTF8 "\x{8ac6}\n"] 
+2

Merci pour la réponse. La documentation perl dit que c'est correct d'utiliser les fonctions du module utf8. La phrase après votre citation est "Les fonctions d'utilité décrites ci-dessous sont directement utilisables sans utilisation utf8;", ie on ne devrait pas "utiliser" (perl mot-clé) le pragma utf8 si on n'en a pas besoin, mais on peut l'utiliser) sa fonction. En outre, je me rends compte que "eaab860a" est le codage unique. Mon fichier contient les octets "c3a8c2abc28b0a", qui sont le double codage. Il s'avère que ma confusion provient d'un bug dans la fonction "longueur". Voir http://www.perlmonks.org/?node_id=874996 – Matt

+8

Il dit en fait "Ne pas utiliser ce pragma pour autre chose que de dire à Perl que votre script est écrit en UTF-8. Les fonctions utilitaires décrites ci-dessous sont directement utilisables sans utilisez utf8 ;. ", ce qui ne signifie clairement pas" vous n'êtes pas censé utiliser les fonctions du module utf8 pragma ". Cela signifie que vous n'avez pas besoin d'utiliser le pragma pour importer les fonctions. –