2010-06-03 809 views
8

¿Cómo se representan las cadenas perl internamente? ¿Qué codificación se usa? ¿Cómo manejo diferentes codificaciones correctamente?Perl strings internals

He estado usando perl durante bastante tiempo, pero no incluye una gran cantidad de manejo de cadenas en diferentes codificaciones, y cuando encontré un problema menor que tenía algo que ver con las codificaciones usualmente recurrí a algunos acciones chamánicas

Hasta este momento, pensé en cadenas perl como secuencias de bytes, que encajaban bastante bien para mis tareas. Ahora necesito procesar algo del archivo codificado en UTF-8 y aquí comienza un problema.

En primer lugar, leí archivo en cadena como esta:

open(my $in, '<', $ARGV[0]) or die "cannot open file $ARGV[0] for reading"; 
binmode($in, ':utf8'); 

my $contents; 

{ 
    local $/; 
    $contents = <$in>; 
} 

close($in); 

entonces simplemente imprimirlo:

print $contents; 

y me da dos cosas: una advertencia Wide character in print at <scriptname> line <n> y un cubo de basura en la consola. Así que puedo concluir que las cadenas perl tienen un concepto de "carácter" que puede ser "ancho" o no, pero cuando se imprimen estos caracteres "anchos" se representan en la consola como múltiples bytes, no como un solo "carácter". (Me pregunto ahora por qué toda mi experiencia previa con archivos binarios funcionó como esperaba que funcionara sin ningún problema de "carácter").

¿Por qué entonces veo basura en la consola? Si perl almacena cadenas como caracteres en alguna codificación conocida, no creo que haya un gran problema para descubrir la codificación de la consola e imprimir el texto correctamente. (Yo uso Windows, por cierto).

Si perl almacena cadenas como secuencias de caracteres de ancho variable (por ejemplo, utilizando la misma codificación UTF-8), ¿por qué se hace de esta manera? Desde mi experiencia en C, manejar cuerdas es DOLOR.

Actualización.

Utilizo dos computadoras para probar, una ejecuta Windows 7 x64 con el paquete de idioma inglés instalado, pero con la configuración regional rusa (entonces tengo cp866 como página de códigos OEM y cp1251 como ANSI) con ActivePerl 5.10.1 x64; otro ejecuta la localización rusa de Windows XP de 32 bits con Cygwin Perl 5.10.0.

Gracias a los enlaces, ahora tengo una comprensión mucho más sólida de lo que está sucediendo y cómo deben hacerse las cosas.

Respuesta

4

Establecer utf8 antes de leer desde el archivo es bueno, decodifica automágicamente los bytes en la codificación interna. (Que también es UTF-8 pero no necesita saberlo, y no debe confiar en él.)

Antes de imprimir, es necesario volver a codificar los caracteres en bytes.

use Encode; 
utf8::encode($contents); 

También hay una forma de codificación de dos argumentos, para otras codificaciones que Unicode. (Esa oración se hace eco demasiado, ¿no?)

Aquí hay una buena referencia. (Hubiera sido más, pero es mi primera publicación.) Consulte también Perlunitut y el artículo Unicode sobre Joel en Software.

http://www.ahinea.com/en/tech/perl-unicode-struggle.html

Ah, y se debe utilizar cadenas multibyte, porque de lo contrario no se trata sólo de Unicode.

+0

Por cadenas de múltiples bytes me refiero a la codificación de ancho variable. – n0rd

+0

De todos modos, no entiendo por qué tengo que hacer la conversión explícitamente: especifiqué la codificación de datos de entrada ¿por qué tengo que tomar algunos pasos adicionales? – n0rd

+2

Has especificado la codificación de entrada. Tú haces tus cosas. Luego, especifica la codificación de salida. Los artículos a los que me refería explican mejor, debería pensar. – dylan

2

Debe mencionar sus versiones actuales de Windows y Perl ya que esto realmente depende de sus versiones usadas y paquetes de idiomas instalados. De lo contrario
echar un vistazo a las instrucciones de PerlUnicode primera -

Perl utiliza caracteres lógicamente a escala para representar cadenas internamente.

confirmará sus declaraciones.

Windows no instala completamente todos los caracteres UTF8, por lo que esta podría ser la razón de su problema. Es posible que deba instalar un paquete de idioma adicional.

+0

Su penúltima oración no tiene ningún sentido. Parece que hace referencia a las fuentes, pero esto no tiene nada que ver con las codificaciones. – daxim

4

Las cadenas Perl se almacenan internamente en una de dos codificaciones, ya sea una codificación nativa de 8 bytes orientada a bytes o UTF-8. Para la comparabilidad hacia atrás, la suposición es que todas las E/S y cadenas están en codificación nativa, a menos que se especifique lo contrario. La codificación nativa suele ser ASCII de 8 bits, pero puede cambiarse con use locale.

En su ejemplo, llama a binmode en su manejador de entrada cambiándolo para usar la semántica :utf8. Un efecto de esto es que todas las cadenas leídas de este identificador se codificarán como UTF-8. print escribe a STDOUT de forma predeterminada, y STDOUT se predetermina a la espera de caracteres codificados nativos.

Perl en un intento de hacer lo correcto permitirá que una cadena UTF-8 se envíe a una salida codificada nativa, pero si no hay codificación adjunta a ese identificador, entonces tiene que adivinar cómo generar bytes múltiples personajes y es casi seguro que adivinar mal. Eso es lo que significa la advertencia, un carácter multibyte fue enviado a una secuencia esperando solo caracteres de un solo byte y el resultado fue que el personaje probablemente fue dañado en la traducción.

Según lo que desee lograr, puede usar el módulo Encode mencionado por dylan para convertir los datos UTF-8 a un juego de caracteres de un solo byte que se puede imprimir de forma segura o si sabe que lo que está adjunto al STDOUT puede manejar UTF-8 puede usar binmode(STDOUT, ':utf8'); para decirle a Perl que desea que todos los datos enviados a STDOUT se envíen como UTF-8.

+0

Si la codificación definitiva era ASCII de 8 bits (o cualquier otra codificación de 8 bits), Perl imprime cadenas UTF-8 como bytes sin formato (es decir, imprime dos caracteres a consola para cada carácter cirílico en cadena impresa) en lugar de imprimir el resultado de la transcodificación esa codificación que tendría exactamente la misma cantidad de caracteres que en la cadena original? – n0rd

+1

@ n0rd una cadena UTF-8 no está en bytes desde la perspectiva perl, son sus caracteres. Un resultado extraño de este IIRC es que cuando se imprime en un manipulador sin codificación definida, truncará los puntos de código Unicode mayores de 255 a solo los 8 bits inferiores. –