¿Por qué Perl moderno evita UTF-8 por defecto?
unicode (7)
Me pregunto por qué la mayoría de las soluciones modernas creadas con Perl no habilitan UTF-8 de forma predeterminada.
Entiendo que hay muchos problemas heredados para los scripts principales de Perl, donde pueden romper cosas. Pero, desde mi punto de vista, en el siglo XXI, los grandes proyectos nuevos (o proyectos con una gran perspectiva) deberían hacer que su software sea una prueba de cero. Todavía no veo que suceda. Por ejemplo, Moose habilita estrictos y advertencias, pero no Unicode . Modern::Perl reduce la placa de la caldera, pero no maneja el UTF-8.
¿Por qué? ¿Existen algunas razones para evitar UTF-8 en los proyectos Perl modernos en el año 2011?
Comentar @tchrist es demasiado largo, así que lo estoy agregando aquí.
Parece que no me aclaré. Déjame intentar agregar algunas cosas.
Tchrist y yo vemos la situación de manera similar, pero nuestras conclusiones están completamente en los extremos opuestos. Estoy de acuerdo, la situación con Unicode es complicada, pero es por eso que nosotros (los usuarios y los programadores de Perl) necesitamos alguna capa (o pragma) que hace que el manejo de UTF-8 sea tan fácil como debe ser en la actualidad.
Tchrist señaló muchos aspectos para cubrir, leeré y pensaré sobre ellos durante días o incluso semanas. Aún así, este no es mi punto. tchrist intenta probar que no hay una sola manera "habilitar UTF-8". No tengo mucho conocimiento para discutir con eso. Por lo tanto, me atengo a los ejemplos vivos.
Rakudo con Rakudo y UTF-8 estaba allí cuando lo necesitaba . No tuve ningún problema, simplemente funcionó. Tal vez haya alguna limitación en algún lugar más profundo, pero al comienzo, todo lo que probé funcionó como esperaba.
¿No debería ser esa una meta en el Perl 5 moderno también? Lo enfatizo más: no estoy sugiriendo que UTF-8 sea el conjunto de caracteres predeterminado para Core Perl, sugiero la posibilidad de activarlo en un instante para aquellos que desarrollan nuevos proyectos.
Otro ejemplo, pero con un tono más negativo. Los marcos deberían facilitar el desarrollo. Hace algunos años, probé frameworks web, pero simplemente los tiré porque "habilitar UTF-8" era muy oscuro. No encontré cómo y dónde conectar el soporte Unicode. Tomó tanto tiempo que me resultó más fácil seguir el camino anterior. Ahora que vi que había una recompensa para tratar el mismo problema con Mason 2: ¿Cómo limpiar Mason2 UTF-8? . Entonces, es un marco bastante nuevo, pero usarlo con UTF-8 necesita un conocimiento profundo de sus componentes internos. Es como un gran letrero rojo: ¡PARE, no me use!
Realmente me gusta Perl. Pero tratar con Unicode es doloroso. Todavía me encuentro corriendo contra las paredes. De alguna manera, Chris tiene razón y responde a mis preguntas: los nuevos proyectos no atraen a UTF-8 porque es demasiado complicado en Perl 5.
🐪🐫🐪🐫🐪
𝓔𝓭𝓲𝓽: 𝙎𝙞𝙢𝙥𝙡𝙚𝙨𝙩 ℞ : 𝟕 𝘿𝙞𝙨𝙘𝙧𝙚𝙩𝙚 𝙍𝙚𝙘𝙤𝙢𝙢𝙚𝙣𝙙𝙖𝙩𝙞𝙤𝙣𝙨
Configure su
PERL_UNICODE
envariable aAS
. Esto hace que todos los scripts de Perl descodifiquen@ARGV
como@ARGV
UTF ‑ 8, y establece la codificación de los tres stdin, stdout y stderr en UTF-8. Ambos son efectos globales, no léxicos.En la parte superior de su archivo fuente (programa, módulo, biblioteca, hickey), afirme de manera prominente que está ejecutando Perl versión 5.12 o superior a través de:
use v5.12; # minimal for unicode string feature
use v5.14; # optimal for unicode string feature
Habilitar advertencias, ya que la declaración anterior solo habilita restricciones y características, no advertencias. También sugiero que se promocionen las advertencias de Unicode en excepciones, así que use ambas líneas, no solo una de ellas. Sin embargo, tenga en cuenta que, en v5.14, la clase de advertencia
utf8
comprende otras tres advertencias secundarias que se pueden habilitar por separado:nonchar
,surrogate
ynon_unicode
. Estos pueden desear ejercer un mayor control sobre.use warnings;
use warnings qw( FATAL utf8 );
Declare que esta unidad fuente está codificada como UTF-8. Aunque alguna vez este pragma hizo otras cosas, ahora cumple este único propósito singular y no otro:
use utf8;
Declare que cualquier cosa que abra un identificador de archivos dentro de este ámbito léxico pero no en otro lugar es asumir que esa secuencia está codificada en UTF-8 a menos que le indique lo contrario. De esa manera no afectará el código de otro módulo u otro programa.
use open qw( :encoding(UTF-8) :std );
Habilitar los caracteres nombrados a través de
/N{CHARNAME}
.use charnames qw( :full :short );
Si tiene un manejador de
DATA
, debe establecer explícitamente su codificación. Si desea que esto sea UTF-8, entonces diga:binmode(DATA, ":encoding(UTF-8)");
Por supuesto, no hay fin a otros asuntos con los que eventualmente se encuentre preocupado, pero estos serán suficientes para aproximarse a la meta estatal de "hacer que todo funcione con UTF-8", aunque por un sentido un tanto debilitado de esos términos.
Otro pragma, aunque no está relacionado con Unicode, es:
use autodie;
Se recomienda encarecidamente.
𝕹
Diciendo que "Perl debería [de alguna manera! ] habilitar Unicode de forma predeterminada "ni siquiera comienza a pensar sobre cómo decir lo suficiente como para ser marginalmente útil en algún tipo de caso raro y aislado. Unicode es mucho más que un repertorio de personajes más grande; También es cómo todos esos personajes interactúan de muchas, muchas maneras.
Incluso las medidas mínimas de mente simple que (algunas) personas parecen pensar que quieren están garantizadas para romper miles de líneas de código, código que no tiene oportunidad de "actualizarse" a su nueva y moderna modernidad de Brave New World .
Es mucho más complicado de lo que la gente pretende. He pensado en esto muchísimo en los últimos años. Me encantaría que me mostraran que estoy equivocado. Pero no creo que lo esté. Unicode es fundamentalmente más complejo que el modelo que le gustaría imponer, y aquí hay una complejidad que nunca puede barrer debajo de la alfombra. Si lo intentas, romperás tu propio código o el de alguien más. En algún momento, simplemente tiene que desglosarse y aprender de qué se trata Unicode. No puedes pretender que es algo que no es.
🐪 hace todo lo posible para hacer que Unicode sea fácil, mucho más que cualquier otra cosa que haya usado. Si crees que esto es malo, intenta algo más por un tiempo. Luego regrese a: ya sea que habrá regresado a un mundo mejor, o bien traerá el conocimiento del mismo para que podamos hacer uso de su nuevo conocimiento para mejorar en estas cosas.
𝕴𝖉𝖊𝖆𝖘
Como mínimo, aquí hay algunas cosas que parecen requerirse para que 🐪 "habilite Unicode de forma predeterminada", como lo indica:
Todo el código fuente de 🐪 debe estar en UTF-8 por defecto. Puede obtenerlo con
use utf8
oexport PERL5OPTS=-Mutf8
.El controlador 🐪
DATA
debe ser UTF-8. Tendrá que hacer esto por paquete, como enbinmode(DATA, ":encoding(UTF-8)")
.Los argumentos del programa para 🐪 scripts deben entenderse como UTF-8 por defecto.
export PERL_UNICODE=A
, operl -CA
, oexport PERL5OPTS=-CA
.La entrada estándar, la salida y las secuencias de error deben predeterminarse a UTF-8.
export PERL_UNICODE=S
para todos ellos, oI
,O
y / oE
para solo algunos de ellos. Esto es comoperl -CS
.Cualquier otra manija abierta por 🐪 debe considerarse UTF-8 a menos que se indique lo contrario;
export PERL_UNICODE=D
o coni
yo
para determinados de estos;export PERL5OPTS=-CD
funcionaría. Eso hace-CSAD
para todos ellos.Cubre ambas bases más todas las secuencias que abra con la
export PERL5OPTS=-Mopen=:utf8,:std
. Ver uniquote .No querrá perderse los errores de codificación UTF-8. Intente
export PERL5OPTS=-Mwarnings=FATAL,utf8
. Y asegúrese de que sus flujos de entrada siempre seanbinmode
d para:encoding(UTF-8)
, no solo para:utf8
.Los puntos de código entre 128–255 deben entenderse por 🐪 como los puntos de código Unicode correspondientes, no solo valores binarios sin propiedad.
use feature "unicode_strings"
oexport PERL5OPTS=-Mfeature=unicode_strings
. Eso hará queuc("/xDF") eq "SS"
y"/xE9" =~ //w/
. Una simpleexport PERL5OPTS=-Mv5.12
o mejor también obtendrá eso.Los caracteres Unicode nombrados no están habilitados de manera predeterminada, así que agregue
export PERL5OPTS=-Mcharnames=:full,:short,latin,greek
o algoexport PERL5OPTS=-Mcharnames=:full,:short,latin,greek
. Ver uninames y tcgrep .Casi siempre necesita acceder a las funciones del módulo estándar
Unicode::Normalize
varios tipos de descomposiciones.export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD
, y luego siempre ejecutar cosas entrantes a través de NFD y cosas salientes de NFC. No hay una capa de E / S para estos aún que yo sepa, pero vea nfc , nfd , nfkd y nfkc .Las comparaciones de cadenas en 🐪 usando
eq
,ne
,lc
,cmp
,sort
, & c & cc siempre son incorrectas. Entonces, en lugar de@a = sort @b
, necesitas@a = Unicode::Collate->new->sort(@b)
. También podría agregar eso a suexport PERL5OPTS=-MUnicode::Collate
. Puede almacenar en caché la clave para comparaciones binarias.🐪 incorporados como
printf
ywrite
hacen lo incorrecto con los datos Unicode.Unicode::GCString
usar el móduloUnicode::GCString
para el primero, y tanto eso como el móduloUnicode::LineBreak
también para el segundo. Ver uwc y unifmt .Si quiere que cuenten como enteros, entonces tendrá que ejecutar sus capturas
/d+
través de la funciónUnicode::UCD::num
porque el atoi (3) incorporado de 🐪 no es lo suficientemente inteligente.Vas a tener problemas con el sistema de archivos en ystem sistemas de archivos. Algunos sistemas de archivos imponen silenciosamente una conversión a NFC; otros imponen silenciosamente una conversión a NFD. Y otros hacen otra cosa todavía. Algunos incluso ignoran el asunto por completo, lo que conduce a problemas aún mayores. Así que tienes que hacer tu propio manejo de NFC / NFD para mantenerte sano.
Todo su código involving que involucre
az
oAZ
y DEBE SER CAMBIADO , incluyendom//
,s///
ytr///
. Debe destacarse como una bandera roja que grita que su código está roto. Pero no está claro cómo debe cambiar. Obtener las propiedades correctas y comprender sus argumentos es más difícil de lo que piensas. Uso unichars y uniprops todos los días.El código que usa
/p{Lu}
es casi tan incorrecto como el código que usa[A-Za-z]
. Debe usar/p{Upper}
lugar, y saber la razón. Sí,/p{Lowercase}
y/p{Lower}
son diferentes de/p{Ll}
y/p{Lowercase_Letter}
.El código que usa
[a-zA-Z]
es aún peor. Y no puede usar/pL
o/p{Letter}
; necesita usar/p{Alphabetic}
. No todas las letras son letras, ¿sabes?Si está buscando 🐪 variables con
/[/$/@/%]/w+/
, entonces tiene un problema. Debe buscar/[/$/@/%]/p{IDS}/p{IDC}*/
, e incluso eso no está pensando en las variables de puntuación o las variables de paquete.Si está buscando espacios en blanco, debe elegir entre
/h
/v
, dependiendo. Y nunca debes usar/s
, ya que NO SIGNIFICA[/h/v]
, contrariamente a la creencia popular.Si está utilizando
/n
para un límite de línea, o incluso/r/n
, entonces lo está haciendo mal. Tienes que usar/R
, que no es lo mismo!Si no sabe cuándo y si debe llamar a Unicode::Stringprep , es mejor que aprenda.
Las comparaciones que no distinguen entre mayúsculas y minúsculas necesitan verificar si dos cosas son las mismas letras sin importar sus signos diacríticos y demás. La forma más sencilla de hacerlo es con el módulo estándar Unicode :: Collate .
Unicode::Collate->new(level => 1)->cmp($a, $b)
. También hay métodoseq
y similares, y probablemente debería aprender sobre los métodos dematch
ysubstr
, también. Estos tienen claras ventajas sobre los 🐪 incorporados.A veces eso todavía no es suficiente, y necesita el módulo Unicode :: Collate :: Locale en su lugar, como en
Unicode::Collate::Locale->new(locale => "de__phonebook", level => 1)->cmp($a, $b)
lugar. Considere queUnicode::Collate::->new(level => 1)->eq("d", "ð")
es verdadero, peroUnicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð")
es falso. De manera similar, "ae" y "æ" soneq
si no usa locales, o si usa el inglés, pero son diferentes en el locale islandés. ¿Ahora que? Es duro, te lo digo. Puedes jugar con unifmt para probar algunas de estas cosas.Considere cómo hacer coincidir el patrón CVCV (consonante, vocal, consonante, vocal) en la cadena " niño ". Su forma NFD, que mejor se acuerda de haberla puesto, se convierte en "nin / x {303} o". ¿Ahora que vas a hacer? Incluso pretendiendo que una vocal es
[aeiou]
(lo cual es incorrecto, por cierto), tampoco podrá hacer algo como(?=[aeiou])/X)
, porque incluso en NFD un punto de código como '' ø '' no se descompone ! Sin embargo, se probará igual a una ''o'' usando la comparación UCA que acabo de mostrarte. No puedes confiar en NFD, tienes que confiar en UCA.
𝔸
Y eso no es todo. Hay millones de suposiciones rotas que la gente hace sobre Unicode. Hasta que entiendan estas cosas, su código 🐪 se romperá.
Código que asume que puede abrir un archivo de texto sin especificar que la codificación está rota.
El código que asume que la codificación predeterminada es algún tipo de codificación de plataforma nativa que está rota.
El código que asume que las páginas web en japonés o chino ocupan menos espacio en UTF-16 que en UTF-8 es incorrecto.
El código que asume que Perl usa UTF-8 internamente es incorrecto.
El código que asume que los errores de codificación siempre generarán una excepción es incorrecto.
El código que asume que los puntos de código Perl están limitados a 0x10_FFFF es incorrecto.
El código que asume que puede establecer
$/
en algo que funcionará con cualquier separador de línea válido es incorrecto.El código que asume la igualdad de ida y vuelta en la presentación de casos, como
lc(uc($s)) eq $s
ouc(lc($s)) eq $s
, está completamente roto y equivocado. Considere queuc("σ")
yuc("ς")
son ambos"Σ"
, perolc("Σ")
no puede devolver ambos.El código que asume que cada punto de código en minúscula tiene uno distinto en mayúscula, o viceversa, está roto. Por ejemplo,
"ª"
es una letra minúscula sin mayúsculas; mientras que tanto"ᵃ"
como"ᴬ"
son letras, pero no son letras minúsculas; sin embargo, ambos son puntos de código en minúsculas sin las correspondientes versiones en mayúsculas. ¿Lo tengo? No son/p{Lowercase_Letter}
, a pesar de ser/p{Letter}
y/p{Lowercase}
.El código que supone que cambiar el caso no cambia la longitud de la cadena se rompe.
El código que asume que solo hay dos casos está roto. También hay titlecase.
El código que asume que solo las letras tienen un caso está roto Más allá de las letras, resulta que los números, los símbolos e incluso las marcas tienen mayúsculas. De hecho, cambiar el caso puede incluso hacer que algo cambie su categoría general principal, como que
/p{Mark}
convierta en/p{Letter}
. También puede hacer que cambie de un script a otro.El código que asume que el caso nunca es dependiente de la ubicación está roto
El código que asume que Unicode da un higo sobre las configuraciones regionales POSIX está roto.
El código que asume que puede eliminar los signos diacríticos para obtener cartas ASCII de base es malo, aún así, roto, con daño cerebral, incorrecto y justificación para la pena capital.
Código que asume que los signos diacríticos
/p{Diacritic}
y las marcas/p{Mark}
son lo mismo si están rotos.El código que asume que
/p{GC=Dash_Punctuation}
cubre tanto como se rompe/p{Dash}
.El código que asume que los guiones, guiones y menos son lo mismo entre sí, o que solo hay uno de cada uno, está roto y equivocado.
El código que asume que cada punto de código ocupa no más de una columna de impresión está roto.
El código que asume que todos los caracteres
/p{Mark}
ocupan cero columnas de impresión está roto.El código que asume que los caracteres que se parecen son iguales está roto.
El código que asume que los caracteres que no se parecen no son iguales está roto.
El código que asume que hay un límite en el número de puntos de código en una fila que solo puede coincidir con una
/X
es incorrecto.El código que asume que
/X
nunca puede comenzar con un carácter/p{Mark}
es incorrecto.El código que asume que
/X
nunca puede contener dos caracteres que no sean/p{Mark}
es incorrecto.El código que asume que no puede usar
"/x{FFFF}"
es incorrecto.El código que asume que un punto de código no BMP que requiere dos unidades de código UTF-16 (sustituto) codificará a dos caracteres UTF-8 separados, uno por unidad de código, es incorrecto. No lo hace: codifica a punto de código único.
El código que transcodifica de UTF-16 o UTF-32 con listas de materiales iniciales en UTF-8 se rompe si coloca una lista de materiales al comienzo del UTF-8 resultante. Esto es tan estúpido que el ingeniero debería tener sus párpados retirados.
El código que asume que CESU-8 es una codificación UTF válida es incorrecto. Del mismo modo, el código que piensa que la codificación U + 0000 como
"/xC0/x80"
es UTF-8 está roto y es incorrecto. Estos chicos también merecen el tratamiento del párpado.El código que asume que los caracteres como
>
siempre apunta a la derecha y<
siempre apunta a la izquierda son incorrectos, porque de hecho no lo hacen.Código que asume que si primero genera el carácter
X
y luego el carácterY
, se mostrará queXY
es incorrecto. A veces no lo hacen.El código que asume que ASCII es lo suficientemente bueno para escribir en inglés correctamente es estúpido, miope, analfabeto, quebrantado, malvado e incorrecto. ¡Afuera con sus cabezas! Si eso parece demasiado extremo, podemos llegar a un acuerdo: de ahora en adelante, pueden tipear solo con su dedo gordo del pie (el resto aún debe estar recogido).
El código que asume que todos los puntos de código
/p{Math}
son caracteres visibles es incorrecto.El código que asume que
/w
contiene solo letras, dígitos y guiones bajos es incorrecto.El código que asume que
^
y~
son signos de puntuación es incorrecto.El código que asume que
ü
tiene una diéresis es incorrecto.El código que cree que cosas como
₨
contienen letras en ellos es incorrecto.El código que cree que
/p{InLatin}
es el mismo que/p{Latin}
está gravemente dañado.El código que cree que
/p{InLatin}
es casi siempre útil es casi seguro que está equivocado.El código que cree que si se asigna
$FIRST_LETTER
como la primera letra de un alfabeto y$LAST_LETTER
como la última letra de ese mismo alfabeto,[${FIRST_LETTER}-${LAST_LETTER}]
tiene algún significado, casi siempre está completo, incorrecto, y sin sentido.El código que cree que el nombre de alguien solo puede contener ciertos caracteres es estúpido, ofensivo e incorrecto.
El código que intenta reducir Unicode a ASCII no es simplemente erróneo, nunca se debe permitir que su autor trabaje en la programación nuevamente. Período. Ni siquiera estoy seguro de que se les permita ver de nuevo, ya que obviamente no les ha servido de mucho hasta ahora.
El código que cree que hay una manera de fingir que las codificaciones de archivos de texto no existen está roto y es peligroso. También podría sacar el otro ojo, también.
Código que convierte caracteres desconocidos a
?
está roto, es estúpido, está en desacuerdo y es contrario a la recomendación estándar, que dice ¡ NO HACER ESO! RTFM por qué no.El código que cree que puede adivinar de manera confiable que la codificación de un archivo de texto no marcado es culpable de una mezcla fatal de arrogancia e ingenuidad que solo un rayo de Zeus reparará.
Código que cree que puede usar width anchos de
printf
para rellenar y justificar que los datos de Unicode están rotos e incorrectos.Código que cree que una vez que creas exitosamente un archivo con un nombre dado, que cuando ejecutas
ls
oreaddir
en su directorio adjunto, en realidad encontrarás que ese archivo con el nombre con el que lo creaste está defectuoso, roto y equivocado. Deja de ser sorprendido por esto!El código que cree que UTF-16 es una codificación de ancho fijo es estúpido, está roto y es incorrecto. Revocar su licencia de programación.
El código que trata los puntos de código de un plano de forma diferente a los de cualquier otro plano está ipso facto roto y es incorrecto. Volver a la escuela.
Código que cree que cosas como
/s/i
solo pueden coincidir con"S"
o"s"
están rotas y son incorrectas. Te sorprenderias.El código que usa
/PM/pM*
para encontrar grupos de grafemas en lugar de usar/X
está roto y equivocado.Las personas que desean volver al mundo ASCII deben ser alentadas de todo corazón a que lo hagan, y en honor a su gloriosa actualización, se les debe proporcionar gratis una máquina de escribir manual pre-eléctrica para todas sus necesidades de ingreso de datos. Los mensajes que se les envíen deben enviarse a través de un telégrafo de s a 40 caracteres por línea y entregados por un mensajero. DETENER.
🐪 𝕭𝖔𝖎𝖑𝖊𝖗⸗𝖕𝖑𝖆𝖙𝖊
Mi propio plato en estos días tiende a verse así:
use 5.014;
use utf8;
use strict;
use autodie;
use warnings;
use warnings qw< FATAL utf8 >;
use open qw< :std :utf8 >;
use charnames qw< :full >;
use feature qw< unicode_strings >;
use File::Basename qw< basename >;
use Carp qw< carp croak confess cluck >;
use Encode qw< encode decode >;
use Unicode::Normalize qw< NFD NFC >;
END { close STDOUT }
if (grep //P{ASCII}/ => @ARGV) {
@ARGV = map { decode("UTF-8", $_) } @ARGV;
}
$0 = basename($0); # shorter messages
$| = 1;
binmode(DATA, ":utf8");
# give a full stack dump on any untrapped exceptions
local $SIG{__DIE__} = sub {
confess "Uncaught exception: @_" unless $^S;
};
# now promote run-time warnings into stackdumped exceptions
# *unless* we''re in an try block, in which
# case just generate a clucking stackdump instead
local $SIG{__WARN__} = sub {
if ($^S) { cluck "Trapped warning: @_" }
else { confess "Deadly warning: @_" }
};
while (<>) {
chomp;
$_ = NFD($_);
...
} continue {
say NFC($_);
}
__END__
𝕾
No sé cuánto más puede obtener "Unicode predeterminado en 🐪" que lo que he escrito. Bueno, sí lo hago: debería usar Unicode::Collate
y Unicode::LineBreak
, también. Y probablemente más.
Como puede ver, hay demasiadas cosas de Unicode de las que realmente tiene que preocuparse para que exista algo así como "predeterminado para Unicode".
Lo que va a descubrir, tal como lo hicimos en la versión 5.8, es que es simplemente imposible imponer todas estas cosas en un código que no ha sido diseñado desde el principio para dar cuenta de ellas. Tu egoísmo bien intencionado acaba de romper el mundo entero.
E incluso una vez que lo haces, todavía hay problemas críticos que requieren una gran cantidad de pensamiento para hacerlo bien. No hay interruptor que puedas voltear. Nada más que cerebro, y me refiero a cerebro real , será suficiente aquí. Hay un montón de cosas que tienes que aprender. Modulo el retiro a la máquina de escribir manual, simplemente no puede esperar pasar sigilosamente en la ignorancia. Este es el siglo 21ˢᵗ, y no puedes desatar a Unicode por ignorancia voluntaria.
Tienes que aprenderlo. Período. Nunca será tan fácil que "todo simplemente funciona", porque eso garantizará que muchas cosas no funcionen, lo que invalida la suposición de que siempre puede haber una manera de "hacer que todo funcione".
Es posible que pueda obtener algunos valores predeterminados razonables para operaciones muy pocas y muy limitadas, pero no sin pensar mucho más en lo que creo que tiene.
Como solo un ejemplo, el ordenamiento canónico va a causar algunos dolores de cabeza reales. 😭 "/x{F5}"
''õ'' , "o/x{303}"
''õ'' , "o/x{303}/x{304}"
''ȭ'' , y "o/x{304}/x{303}"
''ō̃'' debería coincidir con ''õ'' , pero ¿cómo demonios vas a hacer eso? Esto es más difícil de lo que parece, pero es algo que debe tener en cuenta. 💣
Si hay algo que sé sobre Perl, es lo que hacen y no hacen los bits de Unicode, y esto es lo que te prometo: “̲ɪ̲s̲ ̲ɴ̲ᴏ̲ ̲U̲ɴ̲ɪ̲ᴄ̲ᴏ̲ᴅ̲ᴇ̲ ̲ᴍ̲ᴀ̲ɢ̲ɪ̲ᴄ̲ ̲ʙ̲ᴜ̲ʟ̲ʟ̲ᴇ̲ᴛ̲ ̲” 😞
Usted no puede simplemente cambiar algunos valores predeterminados y obtener una navegación suave. Es cierto que ejecuto 🐪 con PERL_UNICODE
configurado en "SA"
, pero eso es todo, e incluso eso es principalmente para cosas de la línea de comandos. Para un trabajo real, paso por todos los pasos descritos anteriormente, y lo hago muy, ** muy ** cuidadosamente.
😈 ¡ƨdləɥ ƨᴉɥʇ ədoɥ puɐ ʻλɐp əɔᴉu ɐ əʌɐɥ ʻʞɔnl poo⅁ 😈
Hay dos etapas para procesar el texto Unicode. El primero es "cómo puedo ingresarlo y emitirlo 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 ambas cosas, pero la segunda parte es de donde proviene el 99% del texto de su publicación. La mayoría de los programas ni siquiera manejan la E / S correctamente, por lo que es importante comprender que incluso antes de comenzar a preocuparse por la normalización y la intercalación.
Este post pretende resolver ese primer problema.
Cuando lees datos en Perl, no importa qué codificación sea. Asigna algo de memoria y guarda los bytes allí. Si dices print $str
, simplemente borra esos bytes a tu terminal, que probablemente esté configurado para asumir que todo lo que está escrito es UTF-8, y aparecerá el texto.
Maravilloso.
Excepto, no lo es. Si intenta tratar los datos como texto, verá que algo malo está sucediendo. No necesita ir más allá de la length
para ver que lo que Perl piensa acerca de su cadena y lo que usted piensa acerca de su cadena no está de acuerdo. Escriba una sola línea como: perl -E ''while(<>){ chomp; say length }''
perl -E ''while(<>){ chomp; say length }''
y escriba 文字化け
y obtendrá 12 ... no es la respuesta correcta, 4.
Eso es porque Perl asume que tu 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
(o use Encode qw(decode)
, por supuesto). Esa función toma alguna 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 entenderá. 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 hay Encode::decode_utf8
, que asume 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.
Eso, justo ahí, es la solución al 99% de los problemas de Unicode en Perl.
La clave es que siempre 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 se pueden 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 obtener datos de su programa. Eso es fácil de solo dice use Encode qw(encode)
, decidir en qué codificación estarán sus datos (UTF-8 a los terminales que entienden UTF-8, UTF-16 para archivos en Windows, etc.) y luego generar el resultado de la encode($encoding, $data)
lugar de solo $data
.
Esta operación convierte los caracteres de Perl, que es en lo que opera tu 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 nuestros terminales, pero no podemos: solo octetos. Así que tenemos que convertir los 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 de tres temas que hacen que esto sea un poco desafiante. La primera es bibliotecas. ¿Manejan el texto correctamente? La respuesta es ... lo intentan. Si descarga una página web, LWP le devolverá el resultado como texto. Si llama al método correcto en el resultado, es decir (y eso es decoded_content
, no content
, que es solo el flujo de octetos que recibió del servidor). Los controladores de la base de datos pueden ser inestables; Si usa DBD :: SQLite solo con Perl, funcionará, pero si alguna otra herramienta ha puesto texto almacenado como otra codificación distinta de UTF-8 en su base de datos ... bueno ... no se manejará correctamente Hasta que escriba 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á arruinando la codificación en algún lugar. Esa advertencia significa "hey, estás intentando filtrar personajes de Perl al mundo exterior y eso no tiene ningún sentido". Su programa parece funcionar (porque el otro extremo generalmente maneja los caracteres en bruto de Perl correctamente), pero está muy roto y podría dejar de funcionar en cualquier momento. Arreglarlo con una Encode::encode
explícita Encode::encode
!
El segundo problema es el código fuente codificado en UTF-8. A menos que diga que 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 digas algo como my $var = ''ほげ''
, estarás inyectando basura en tu programa que romperá todo de manera horrible. 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 Unicode, y Perl asumió que todo era texto latino-1 o binario.Entonces, cuando los datos entran en su programa y usted comienza a tratarlos como texto, Perl trata cada octeto como un carácter Latin-1. Por eso, cuando pedimos la longitud de "文字 化 け", obtuvimos 12. Perl asumió que estábamos operando en la cadena Latin-1 "æååã" (que es de 12 caracteres, algunos de los cuales no se imprimen).
Esto se denomina "actualización implícita" y es una cosa perfectamente razonable de hacer, pero no es lo que quieres si tu texto no es Latin-1. Por eso es crítico descifrar explícitamente la entrada: si no lo haces, Perl lo hará, y podría hacerlo mal.
La gente se mete en problemas donde la mitad de sus datos es una cadena de caracteres adecuada, y algunos aún son binarios. Perl interpretará la parte que aún es binaria como si fuera texto Latin-1 y luego la combinará con los datos de caracteres correctos. Esto hará que parezca que el manejo correcto de tus personajes rompió tu programa, pero en realidad, simplemente no lo has arreglado lo suficiente.
Aquí hay un ejemplo: usted tiene un programa que lee un archivo de texto codificado en UTF-8, usted coloca un Unicode PILE OF POO
en cada línea y lo imprime. Lo escribes como:
while(<>){
chomp;
say "$_ 💩";
}
Y luego ejecute en algunos datos codificados en 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 concatenación binaria. Estás leyendo octetos del archivo, eliminando a /n
con chomp y luego agregando los bytes en la representación UTF-8 del PILE OF POO
carácter. Cuando revise su programa para decodificar los datos del archivo y codifique la salida, notará que obtiene basura ("ð ©") en lugar del poo. Esto lo llevará a creer que descifrar el archivo de entrada es algo incorrecto. No es.
El problema es que la caca se está actualizando implícitamente como latin-1. Si use utf8
creas el texto literal en lugar del binario, ¡funcionará de nuevo!
(Ese es el problema número uno que veo al ayudar a las personas con Unicode. Hicieron un trabajo correcto y eso rompió su programa. Eso es lo triste de los resultados no definidos: puede tener un programa que funcione durante mucho tiempo, pero cuando comienza 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 por 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 necesitas 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 asumes que sabrá mágicamente qué tipo de texto lo estás alimentando, entonces vas a destruir tu información de forma irrevocable. El hecho de que su programa funcione hoy en su terminal UTF-8 no significa que funcionará mañana en un archivo codificado en UTF-16. ¡Así que protéjase ahora y ahórrese el dolor de cabeza de destruir los datos de sus usuarios!
La parte fácil de manejar Unicode es codificar la salida y descodificar la entrada. La parte difícil es encontrar toda su entrada y salida, y determinar qué codificación es. Pero es por eso que obtienes grandes cantidades de dinero :)
Debería habilitar la función de cadenas Unicode, y esta es la opción predeterminada si usa v5.14;
Realmente no deberías usar identificadores Unicode esp. para el código extranjero a través de utf8, ya que son inseguros en perl5, solo cperl lo hizo bien. Ver, por ejemplo, http://perl11.org/blog/unicode-identifiers.html
Con respecto a utf8 para sus manejadores de archivos / flujos: necesita decidir usted mismo la codificación de sus datos externos. Una biblioteca no puede saber eso, y dado que ni siquiera libc admite utf8, los datos utf8 adecuados son raros. Hay más wtf8, las ventanas aberración de utf8 alrededor.
BTW: Moose no es realmente "Perl moderno", simplemente secuestraron el nombre. Moose es un perfecto perl posmoderno estilo Larry Wall mezclado con el estilo Bjarne Stroustrup, con una aberración ecléctica de la sintaxis perl6 adecuada, por ejemplo, el uso de cadenas para nombres de variables, una sintaxis horrible de campos y una implementación ingenua muy inmadura que es 10 veces más lenta que una Implementación adecuada. cperl y perl6 son las verdaderas perls modernas, donde la forma sigue a la función, y la implementación se reduce y optimiza.
Todos estamos de acuerdo en que es un problema difícil por muchas razones, pero esa es precisamente la razón para tratar de hacerlo más fácil para todos.
Hay un módulo reciente en CPAN, utf8::all , que intenta "activar Unicode. Todo".
Como se ha señalado, mágicamente no puede hacer que todo el sistema (programas externos, solicitudes web externas, etc.) también use Unicode, pero podemos trabajar juntos para hacer herramientas sensatas que faciliten los problemas comunes. Esa es la razón por la que somos programadores.
Si utf8 :: all no hace algo que crees que debería, mejorémoslo para mejorarlo. O hagamos herramientas adicionales que juntas puedan satisfacer las diferentes necesidades de las personas lo mejor posible.
`
Al leer este hilo, a menudo me da la impresión de que las personas están utilizando " UTF-8 " como sinónimo de " Unicode ". Por favor, haga una distinción entre los "puntos de código" de Unicode que son un pariente ampliado del código ASCII y las diversas "codificaciones" de Unicode. Y hay algunos de ellos, de los cuales UTF-8, UTF-16 y UTF-32 son los actuales y algunos más están obsoletos.
Por favor, UTF-8 (así como todas las demás codificaciones ) existen y tienen un significado en la entrada o en la salida solamente. Internamente, desde Perl 5.8.1, todas las cadenas se mantienen como "puntos de código" Unicode. Es cierto que tiene que habilitar algunas funciones como se cubrió con admiración anteriormente.
Creo que malinterpretas a Unicode y su relación con Perl. No importa de qué manera almacene datos, Unicode, ISO-8859-1 , o muchas otras cosas, su programa debe saber cómo interpretar los bytes que recibe como entrada (decodificación) y cómo representar la información que desea generar (codificación ). Entiendes mal esa interpretación y tú cageas los datos. No hay una configuración predeterminada mágica dentro de su programa que le dirá a las cosas fuera de su programa cómo actuar.
Crees que es difícil, probablemente, porque estás acostumbrado a que todo sea ASCII. Todo lo que debería haber pensado fue simplemente ignorado por el lenguaje de programación y todas las cosas con las que tenía que interactuar. Si todo utilizara nada más que UTF-8 y no tuviera otra opción, entonces UTF-8 sería igual de fácil. Pero no todo utiliza UTF-8. Por ejemplo, no quiere que su identificador de entrada piense que está obteniendo octetos UTF-8 a menos que realmente lo sea, y no desea que sus identificadores de salida sean UTF-8 si la cosa que les lee puede manejar UTF-8 . Perl no tiene manera de saber esas cosas. Por eso eres el programador.
No creo que Unicode en Perl 5 sea demasiado complicado. Creo que da miedo y la gente lo evita. Hay una diferencia.Para ese fin, he puesto Unicode en Learning Perl, 6ª edición , y hay muchas cosas de Unicode en la programación efectiva de Perl . Tienes que pasar el tiempo para aprender y entender Unicode y cómo funciona. No vas a poder usarlo de otra manera.
Hay una cantidad realmente horrible de códigos antiguos en el mundo salvaje, muchos de ellos en forma de módulos CPAN comunes. Descubrí que debo ser bastante cuidadoso al habilitar Unicode si uso módulos externos que podrían verse afectados, y todavía estoy tratando de identificar y corregir algunos fallos relacionados con Unicode en varios scripts de Perl que uso regularmente (en particular, iTiVo falla mal en cualquier cosa que no sea ASCII de 7 bits debido a problemas de transcodificación).