Há dois estágios no processamento de texto Unicode. O primeiro é "como posso inseri-lo e produzi-lo sem perder informações". O segundo é "como trato o texto de acordo com as convenções do idioma local".
O post de tchrist cobre ambos, mas a segunda parte é de onde vêm 99% do texto em seu post. A maioria dos programas nem lida com E / S corretamente, por isso é importante entender isso antes mesmo de você começar a se preocupar com normalização e agrupamento.
Este post tem como objetivo resolver esse primeiro problema
Quando você lê dados no Perl, não se importa com a codificação. Ele aloca um pouco de memória e armazena os bytes por lá. Se você disser print $str
, ele apenas divide esses bytes no seu terminal, que provavelmente está configurado para assumir que tudo o que está escrito nele é UTF-8, e seu texto é exibido.
Maravilhoso.
Exceto, não é. Se você tentar tratar os dados como texto, verá que Algo Ruim está acontecendo. Você não precisa ir além de length
ver que o que Perl pensa sobre sua string e o que você pensa sobre sua string discorda. Escreva uma linha como: perl -E 'while(<>){ chomp; say length }'
e digite文字化け
e você obtém 12 ... não a resposta correta, 4.
Isso ocorre porque o Perl assume que sua string não é texto. Você precisa dizer que é um texto antes de fornecer a resposta certa.
Isso é fácil; o módulo Encode tem as funções para fazer isso. O ponto de entrada genérico é Encode::decode
(ouuse Encode qw(decode)
, é claro). Essa função pega alguma string do mundo exterior (o que chamaremos de "octetos", uma maneira extravagante de dizer "bytes de 8 bits"), e a transforma em algum texto que o Perl entenderá. O primeiro argumento é um nome de codificação de caracteres, como "UTF-8" ou "ASCII" ou "EUC-JP". O segundo argumento é a string. O valor de retorno é o escalar Perl que contém o texto.
(Também existe Encode::decode_utf8
, que assume UTF-8 para a codificação.)
Se reescrevermos uma linha:
perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }'
Digitamos 文字 化 け e obtemos "4" como resultado. Sucesso.
Essa é a solução para 99% dos problemas de Unicode no Perl.
A chave é que, sempre que houver texto no seu programa, você deve decodificá-lo. A Internet não pode transmitir caracteres. Os arquivos não podem armazenar caracteres. Não há caracteres no seu banco de dados. Existem apenas octetos, e você não pode tratar octetos como caracteres no Perl. Você deve decodificar os octetos codificados em caracteres Perl com o módulo Encode.
A outra metade do problema é obter dados do seu programa. Isso é fácil; você acabou de use Encode qw(encode)
decidir qual será a codificação dos seus dados (UTF-8 para terminais que compreendem UTF-8, UTF-16 para arquivos no Windows etc.) e, em seguida, produza o resultado em encode($encoding, $data)
vez de apenas produzir $data
.
Essa operação converte os caracteres do Perl, que é o que seu programa opera, em octetos que podem ser usados pelo mundo exterior. Seria muito mais fácil se pudéssemos enviar caracteres pela Internet ou para nossos terminais, mas não podemos: apenas octetos. Portanto, temos que converter caracteres em octetos, caso contrário, os resultados serão indefinidos.
Para resumir: codifique todas as saídas e decodifique todas as entradas.
Agora, falaremos sobre três questões que tornam isso um pouco desafiador. O primeiro são as bibliotecas. Eles lidam com texto corretamente? A resposta é ... eles tentam. Se você baixar uma página da Web, o LWP retornará seu resultado como texto. Se você chamar o método certo para o resultado, isso é (e isso acontece decoded_content
, não content
, que é apenas o fluxo de octetos obtido do servidor). Os drivers de banco de dados podem ser esquisitos; se você usar DBD :: SQLite com apenas Perl, funcionará, mas se alguma outra ferramenta colocar texto armazenado como alguma codificação diferente de UTF-8 em seu banco de dados ... bem ... não será tratado corretamente até você escrever o código para manipulá-lo corretamente.
A saída de dados geralmente é mais fácil, mas se você vê "caracteres largos impressos", sabe que está atrapalhando a codificação em algum lugar. Esse aviso significa "ei, você está tentando vazar caracteres Perl para o mundo exterior e isso não faz nenhum sentido". Seu programa parece funcionar (porque a outra extremidade geralmente manipula os caracteres Perl brutos corretamente), mas está muito danificado e pode parar de funcionar a qualquer momento. Corrija-o com um explícito Encode::encode
!
O segundo problema é o código-fonte codificado em UTF-8. A menos que você diga use utf8
na parte superior de cada arquivo, o Perl não assumirá que seu código-fonte é UTF-8. Isso significa que, toda vez que você diz algo do tipo my $var = 'ほげ'
, está injetando lixo em seu programa, que quebrará tudo horrivelmente. Você não precisa "usar utf8", mas se não o fizer, não deverá usar caracteres não ASCII no seu programa.
O terceiro problema é como o Perl lida com o passado. Há muito tempo, não existia o Unicode, e Perl presumiu que tudo era um texto em latim 1 ou binário. Portanto, quando os dados entram no seu programa e você começa a tratá-los como texto, o Perl trata cada octeto como um caractere latino-1. É por isso que, quando solicitamos o tamanho de "文字 化 we", obtemos 12. Perl presumiu que estávamos operando na string Latin-1 "æååã" (que tem 12 caracteres, alguns dos quais não são impressos).
Isso é chamado de "atualização implícita", e é uma coisa perfeitamente razoável de se fazer, mas não é o que você deseja se o seu texto não for latino-1. É por isso que é fundamental decodificar explicitamente a entrada: se você não fizer isso, o Perl o fará, e isso pode ser errado.
As pessoas enfrentam problemas onde metade dos dados é uma sequência de caracteres adequada e alguns ainda são binários. O Perl interpreta a parte que ainda é binária como se fosse um texto em latim-1 e depois a combina com os dados corretos dos caracteres. Isso fará com que pareça que o manuseio correto de seus personagens interrompeu seu programa, mas, na realidade, você simplesmente não o corrigiu o suficiente.
Aqui está um exemplo: você tem um programa que lê um arquivo de texto codificado em UTF-8, adere um Unicode PILE OF POO
a cada linha e o imprime. Você escreve assim:
while(<>){
chomp;
say "$_ 💩";
}
E, em seguida, execute alguns dados codificados em UTF-8, como:
perl poo.pl input-data.txt
Ele imprime os dados UTF-8 com um cocô no final de cada linha. Perfeito, meu programa funciona!
Mas não, você está apenas fazendo concatenação binária. Você está lendo octetos do arquivo, removendo um \n
com chomp e, em seguida, inserindo os bytes na representação UTF-8 do PILE OF POO
personagem. Ao revisar seu programa para decodificar os dados do arquivo e codificar a saída, você notará que obtém lixo ("ð ©") em vez de cocô. Isso levará você a acreditar que decodificar o arquivo de entrada é a coisa errada a se fazer. Não é.
O problema é que o cocô está sendo implicitamente atualizado como latin-1. Se você use utf8
criar o texto literal em vez de binário, ele funcionará novamente!
(Esse é o problema número um que eu vejo ao ajudar as pessoas com Unicode. Eles fizeram a parte certa e isso interrompeu o programa. O que é triste com resultados indefinidos: você pode ter um programa em funcionamento por um longo tempo, mas quando começa a repará-lo, Não se preocupe, se você estiver adicionando instruções de codificação / decodificação ao seu programa e ele quebrar, isso significa apenas que você tem mais trabalho a fazer. Da próxima vez, quando você criar o Unicode em mente desde o início, será muito facil!)
Isso é realmente tudo o que você precisa saber sobre Perl e Unicode. Se você informar ao Perl quais são seus dados, ele possui o melhor suporte Unicode entre todas as linguagens de programação populares. No entanto, se você presumir que ele saberá magicamente que tipo de texto você está alimentando, você irá lixeira irrevogavelmente seus dados. Só porque seu programa funciona hoje em seu terminal UTF-8 não significa que ele funcionará amanhã em um arquivo codificado em UTF-16. Portanto, proteja-o agora e poupe a dor de cabeça de descartar os dados de seus usuários!
A parte fácil de lidar com o Unicode é codificar a saída e decodificar a entrada. A parte difícil é encontrar todas as suas entradas e saídas e determinar qual é a codificação. Mas é por isso que você ganha muito dinheiro :)