2010-12-02 18 views
7

Estoy teniendo algunos resultados interesantes tratando de discernir las diferencias entre usar Encode::decode("utf8", $var) y utf8::decode($var). Ya he descubierto que llamar al anterior varias veces en una variable eventualmente dará como resultado un error "No se puede decodificar la cadena con caracteres anchos en ...", mientras que el último método funcionará felizmente tantas veces como se desee, simplemente devolviendo falso.Perl: utf8 :: decodificar vs. Codificar :: decodificar

Lo que tengo problemas para entender es cómo la función length arroja resultados diferentes según el método que utilice para decodificar. El problema surge porque estoy tratando con texto utf8 "doblemente codificado" de un archivo externo. Para demostrar este problema, creé un archivo de texto "test.txt" con los siguientes caracteres Unicode en una línea: U + 00e8, U + 00ab, U + 0086, U + 000a. Estos caracteres Unicode son la doble codificación del carácter Unicode U + 8acb, junto con un carácter de nueva línea. El archivo fue codificado en el disco en UTF8. Luego ejecutar el siguiente script Perl:

#!/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"; 

Esto da el siguiente resultado:

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 

Esto es lo que se esperaría. La longitud es originalmente 7 porque perl cree que $ test es solo una serie de bytes. Después de decodificar una vez, Perl sabe que $ test es una serie de caracteres que están codificados en utf8 (es decir, en lugar de devolver una longitud de 7 bytes, Perl devuelve una longitud de 4 caracteres, aunque $ test todavía tiene 7 bytes en la memoria). Después de la segunda decodificación, $ test contiene 4 bytes interpretados como 2 caracteres, que es lo que esperaría ya que Encode :: decode tomó los 4 puntos de código y los interpretó como bytes codificados en utf8, lo que resulta en 2 caracteres. Lo extraño es cuando modifico el código para llamar a utf8 :: decode en su lugar (reemplace all $ test = Encode :: decode ("utf8", $ test); con utf8 :: decode ($ test))

This da salida casi idéntico, sólo el resultado de longitud difiere:

 
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 

parece que perl primera cuenta los bytes antes de decodificar (como se esperaba), y luego cuenta los caracteres después de la primera decodificación, pero luego cuenta los bytes de nuevo después de la segunda decodificación (no esperada). ¿Por qué ocurriría este cambio? ¿Hay un lapso en mi comprensión de cómo funcionan estas funciones de decodificación?

Gracias, Matt

+1

¿Alguna razón por la cual usted está 'requiriendo módulos en vez de' usarlos '? –

+1

No utilicé use utf8 porque al hacerlo perl dice que su código está codificado en utf8, lo que no necesito (http://perldoc.perl.org/utf8.html). Supongo que podría haber codificado use d, pero sucedió que no. – Matt

Respuesta

3

No se supone que deben utilizar las funciones del módulo utf8 pragma. Its documentation dice así:

No use este pragma para nada más que decirle a Perl que su script está escrito en UTF-8.

Always use the Encode module, y también vea la pregunta Checklist for going the Unicode way with Perl. unpack es de muy bajo nivel, ni siquiera le da la comprobación de errores.

va mal con la suposición de que los octects E8 AB 86 0A son el resultado de UTF-8 de doble codificación los personajes y newline. Esta es la representación de una única codificación UTF-8 de estos caracteres. Tal vez toda la confusión de tu parte se debe a ese error.

length está sobrecargado indebidamente, en ciertos momentos determina la longitud en caracteres o la longitud en octetos. Use mejores herramientas como 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

Gracias por la respuesta. La documentación de Perl dice que está bien usar las funciones en el módulo utf8. La frase después de su cita es "Las funciones de utilidad que se describen a continuación son directamente utilizables sin usar utf8;", es decir, uno no debe "usar" (uso de palabras clave por palabra) el pragma utf8 si no es necesario, pero se puede usar (inglés)) su función. Además, me doy cuenta de que "eaab860a" es la codificación única. Mi archivo contiene los octetos "c3a8c2abc28b0a", que son la doble codificación. Resulta que mi confusión proviene de un error en la función de "longitud". Ver http://www.perlmonks.org/?node_id=874996 – Matt

+8

En realidad dice "No use este pragma para nada más que decirle a Perl que su script está escrito en UTF-8. Las funciones de utilidad que se describen a continuación son directamente utilizables sin use utf8 ;. ", que claramente no significa" no debe usar las funciones del módulo utf8 pragma ". Significa que no necesita usar el pragma para importar las funciones. –

Cuestiones relacionadas