Hay dos etapas para procesar texto Unicode. El primero es "cómo puedo ingresarlo y generarlo sin perder información". El segundo es "cómo trato el texto de acuerdo con las convenciones del idioma local".
La publicación de tchrist cubre ambos, pero la segunda parte es de donde proviene el 99% del texto en su publicación. La mayoría de los programas ni siquiera manejan las E / S correctamente, por lo que es importante comprender eso antes de que empiece a preocuparse por la normalización y la clasificación.
Esta publicación tiene como objetivo resolver ese primer problema
Cuando lees datos en Perl, no importa qué codificación tenga. Asigna un poco de memoria y guarda los bytes allí. Si usted diceprint $str
, simplemente borra esos bytes a su terminal, que probablemente está configurado para asumir que todo lo que está escrito en él es UTF-8, y su texto aparece.
Maravilloso.
Excepto que no lo es. Si intenta tratar los datos como texto, verá que algo malo está sucediendo. No necesita ir más allá de length
ver que lo que Perl piensa acerca de su cadena y lo que piensa acerca de su cadena no está de acuerdo. Escribe una frase como: perl -E 'while(<>){ chomp; say length }'
y escribe文字化け
y obtienes 12 ... no es la respuesta correcta, 4.
Eso es porque Perl asume que su cadena no es texto. Tienes que decirle que es texto antes de que te dé la respuesta correcta.
Eso es bastante fácil; el módulo Encode tiene las funciones para hacer eso. El punto de entrada genérico es Encode::decode
(ouse Encode qw(decode)
, por supuesto). Esa función toma una cadena del mundo exterior (lo que llamaremos "octetos", una forma elegante de decir "bytes de 8 bits"), y la convierte en un texto que Perl comprenderá. El primer argumento es un nombre de codificación de caracteres, como "UTF-8" o "ASCII" o "EUC-JP". El segundo argumento es la cadena. El valor de retorno es el escalar Perl que contiene el texto.
(También existe Encode::decode_utf8
, lo que supone UTF-8 para la codificación).
Si reescribimos nuestro one-liner:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Escribimos 文字 化 け y obtenemos "4" como resultado. Éxito.
Esa es la solución al 99% de los problemas de Unicode en Perl.
La clave es que cada vez que ingrese texto en su programa, debe decodificarlo. Internet no puede transmitir caracteres. Los archivos no pueden almacenar caracteres. No hay caracteres en su base de datos. Solo hay octetos, y no puedes tratar los octetos como caracteres en Perl. Debe decodificar los octetos codificados en caracteres Perl con el módulo Encode.
La otra mitad del problema es sacar datos de su programa. Eso es fácil de hacer; solo diga use Encode qw(encode)
, decida en qué codificación estarán sus datos (UTF-8 a terminales que entienden UTF-8, UTF-16 para archivos en Windows, etc.), y luego envíe el resultado en encode($encoding, $data)
lugar de simplemente enviarlo $data
.
Esta operación convierte los caracteres de Perl, que es en lo que opera su programa, en octetos que pueden ser utilizados por el mundo exterior. Sería mucho más fácil si pudiéramos enviar caracteres a través de Internet o a nuestras terminales, pero no podemos: octetos solamente. Entonces tenemos que convertir caracteres a octetos, de lo contrario los resultados no están definidos.
Para resumir: codifique todas las salidas y decodifique todas las entradas.
Ahora hablaremos sobre tres problemas que hacen que esto sea un poco desafiante. El primero son las bibliotecas. ¿Manejan el texto correctamente? La respuesta es ... lo intentan. Si descarga una página web, LWP le devolverá su resultado como texto. Si llama al método correcto en el resultado, eso es (y eso es decoded_content
, no content
, que es solo el flujo de octetos que obtuvo del servidor). Los controladores de la base de datos pueden ser escamosos; si usa DBD :: SQLite solo con Perl, funcionará, pero si alguna otra herramienta ha puesto texto almacenado como una codificación distinta de UTF-8 en su base de datos ... bueno ... no se manejará correctamente hasta que escriba el código para manejarlo correctamente.
La salida de datos suele ser más fácil, pero si ve "caracteres anchos en la impresión", entonces sabe que está confundiendo la codificación en alguna parte. Esa advertencia significa "oye, estás tratando de filtrar personajes de Perl al mundo exterior y eso no tiene ningún sentido". Parece que su programa funciona (porque el otro extremo generalmente maneja los caracteres de Perl sin procesar correctamente), pero está muy roto y podría dejar de funcionar en cualquier momento. Solucionarlo con un explícito Encode::encode
!
El segundo problema es el código fuente codificado UTF-8. A menos que diga use utf8
en la parte superior de cada archivo, Perl no asumirá que su código fuente es UTF-8. Esto significa que cada vez que dices algo así my $var = 'ほげ'
, estás inyectando basura en tu programa que romperá todo horriblemente. No tiene que "usar utf8", pero si no lo hace, no debe usar ningún carácter que no sea ASCII en su programa.
El tercer problema es cómo Perl maneja el pasado. Hace mucho tiempo, no existía el Unicode, y Perl asumió que todo era texto o binario latino-1. Entonces, cuando los datos ingresan a su programa y comienza a tratarlos como texto, Perl trata cada octeto como un carácter latino-1. Es por eso que, cuando preguntamos por la longitud de "文字 化 け", obtuvimos 12. Perl asumió que estábamos operando en la cadena latina-1 "æååã" (que tiene 12 caracteres, algunos de los cuales no se imprimen).
Esto se llama una "actualización implícita", y es una cosa perfectamente razonable de hacer, pero no es lo que desea si su texto no es latino-1. Es por eso que es crítico decodificar explícitamente la entrada: si no lo hace, Perl lo hará, y podría hacerlo mal.
Las personas se encuentran con problemas donde la mitad de sus datos es una cadena de caracteres adecuada, y algunos aún son binarios. Perl interpretará la parte que sigue siendo binaria como si fuera texto latino-1 y luego la combinará con los datos de caracteres correctos. Esto hará que parezca que manejar tus personajes correctamente rompió tu programa, pero en realidad, simplemente no lo has solucionado lo suficiente.
Aquí hay un ejemplo: tiene un programa que lee un archivo de texto codificado en UTF-8, pega un Unicode PILE OF POO
en cada línea y lo imprime. Lo escribes como:
while(<>){
chomp;
say "$_ 💩";
}
Y luego ejecuta algunos datos codificados UTF-8, como:
perl poo.pl input-data.txt
Imprime los datos UTF-8 con una caca al final de cada línea. Perfecto, mi programa funciona!
Pero no, solo estás haciendo una concatenación binaria. Estás leyendo octetos del archivo, eliminando a \n
con chomp y luego añadiendo los bytes en la representación UTF-8 del PILE OF POO
personaje. Cuando revise su programa para decodificar los datos del archivo y codifique la salida, notará que obtiene basura ("ð ©") en lugar de la caca. Esto lo llevará a creer que decodificar el archivo de entrada es algo incorrecto. No es.
El problema es que la caca se está actualizando implícitamente como latin-1. Si desea use utf8
hacer el texto literal en lugar de binario, ¡funcionará nuevamente!
(Ese es el problema número uno que veo cuando ayudo a las personas con Unicode. Se separaron bien y eso rompió su programa. Eso es lo triste de los resultados indefinidos: puedes tener un programa que funciona durante mucho tiempo, pero cuando comienzas a repararlo, se rompe. No se preocupe; si agrega declaraciones de codificación / decodificación a su programa y se rompe, solo significa que tiene más trabajo que hacer. La próxima vez, cuando diseñe con Unicode en mente desde el principio, será ¡más fácil!)
Eso es realmente todo lo que necesita saber sobre Perl y Unicode. Si le dice a Perl cuáles son sus datos, tiene el mejor soporte Unicode entre todos los lenguajes de programación populares. Sin embargo, si asume que mágicamente sabrá qué tipo de texto lo está alimentando, entonces va a tirar a la basura sus datos irrevocablemente. El hecho de que su programa funcione hoy en su terminal UTF-8 no significa que funcionará mañana en un archivo codificado UTF-16. ¡Así que hazlo seguro ahora y ahórrate el dolor de cabeza de destrozar los datos de tus usuarios!
La parte fácil de manejar Unicode es codificar la salida y decodificar la entrada. La parte difícil es encontrar todas sus entradas y salidas, y determinar qué codificación es. Pero es por eso que obtienes mucho dinero :)