Eu me pergunto por que a maioria das soluções modernas criadas usando o Perl não habilita o UTF-8 por padrão.
Entendo que existem muitos problemas herdados para scripts Perl principais, nos quais isso pode quebrar as coisas. Mas, do meu ponto de vista, na 21 st século, novos e grandes projetos (ou projectos com uma grande perspectiva) deve fazer o seu software UTF-8 prova a partir do zero. Ainda não vejo isso acontecendo. Por exemplo, o Moose habilita estritos e avisos, mas não Unicode . Moderno: o Perl também reduz o padrão, mas não o manuseio UTF-8.
Por quê? Existem algumas razões para evitar o UTF-8 em projetos Perl modernos no ano de 2011?
Comentar @tchrist ficou muito tempo, então eu estou adicionando aqui.
Parece que não me deixei claro. Deixe-me tentar adicionar algumas coisas.
Chris e eu vemos a situação da mesma maneira, mas nossas conclusões são completamente opostas. Concordo que a situação com o Unicode é complicada, mas é por isso que nós (usuários e codificadores Perl) precisamos de uma camada (ou pragma) que torne o manuseio de UTF-8 o mais fácil possível nos dias de hoje.
Se Chris apontou muitos aspectos a serem abordados , vou ler e pensar sobre eles por dias ou até semanas. Ainda assim, este não é o meu ponto. O tchrist tenta provar que não existe uma única maneira "de habilitar o UTF-8". Eu não tenho muito conhecimento para discutir isso. Então, continuo a viver exemplos.
Eu brinquei com Rakudo e o UTF-8 estava lá quando eu precisava . Não tive nenhum problema, apenas funcionou. Talvez haja alguma limitação em algum lugar mais profundo, mas, no início, tudo que eu testei funcionou como eu esperava.
Isso também não deveria ser um objetivo no Perl 5 moderno? Enfatizo mais: não estou sugerindo o UTF-8 como o conjunto de caracteres padrão para o Perl principal; sugiro a possibilidade de acioná-lo rapidamente para aqueles que desenvolvem novos projetos.
Outro exemplo, mas com um tom mais negativo. Estruturas devem facilitar o desenvolvimento. Alguns anos atrás, tentei frameworks da web, mas apenas os joguei fora porque "ativar o UTF-8" era muito obscuro. Não encontrei como e onde conectar o suporte a Unicode. Foi tão demorado que achei mais fácil seguir o caminho antigo. Agora eu vi aqui que havia uma recompensa para lidar com o mesmo problema com o Mason 2: Como limpar o Mason2 UTF-8? . Portanto, é uma estrutura bastante nova, mas usá-la com UTF-8 precisa de um conhecimento profundo de seus componentes internos. É como um grande sinal vermelho: PARE, não me use!
Eu realmente gosto de Perl. Mas lidar com Unicode é doloroso. Eu ainda me pego correndo contra as paredes. De alguma forma, o tchrist está certo e responde às minhas perguntas: novos projetos não atraem UTF-8 porque é muito complicado no Perl 5.
Respostas:
℞ ℞ : 𝟕 𝘿𝙞𝙨𝙘𝙧𝙚𝙩𝙚 𝙍𝙚𝙘𝙤𝙢𝙢𝙚𝙣𝙙𝙖𝙩𝙞𝙤𝙣𝙨
Defina seu
PERL_UNICODE
envariável comoAS
. Isso faz com que todos os scripts Perl decodifiquem@ARGV
como strings UTF ‑ 8 e define a codificação dos três stdin, stdout e stderr como UTF ‑ 8. Ambos são efeitos globais, não lexicais.Na parte superior do seu arquivo de origem (programa, módulo, biblioteca,
do
hickey), afirme com destaque que você está executando o perl versão 5.12 ou superior através de:Ative avisos, uma vez que a declaração anterior ativa apenas restrições e recursos, não avisos. Também sugiro promover avisos Unicode em exceções, portanto, use essas duas linhas, não apenas uma delas. Nota, contudo, que sob v5.14, a
utf8
classe de advertência compreende três outros subwarnings que podem ser ativados separadamente:nonchar
,surrogate
, enon_unicode
. Estes você pode querer exercer maior controle sobre.Declare que esta unidade de origem está codificada como UTF ‑ 8. Embora outrora esse pragma fizesse outras coisas, agora serve a esse único propósito único e a nenhum outro:
Declare que qualquer coisa que abra um identificador de arquivo nesse escopo lexical, mas não em outro lugar, deve assumir que esse fluxo é codificado em UTF-8, a menos que você indique o contrário. Dessa forma, você não afeta o código de outro módulo ou outro programa.
Ativar caracteres nomeados via
\N{CHARNAME}
.Se você tem um
DATA
identificador, deve definir explicitamente sua codificação. Se você deseja que este seja UTF ‑ 8, diga:É claro que não há fim de outros assuntos com os quais você possa se preocupar, mas estes serão suficientes para aproximar a meta do estado de "fazer tudo funcionar apenas com a UTF-8", embora para um sentido um pouco enfraquecido desses termos.
Um outro pragma, embora não seja relacionado ao Unicode, é:
É fortemente recomendado.
🌴 🐪🐫🐪 🌞 𝕲𝖔 𝕿𝖍𝖔𝖚 𝕯𝖔 𝕷𝖎𝖐𝖊𝖜𝖎𝖘𝖊 🌞 🐪🐫🐪 🐁
🎁 🐪 𝕭𝖔𝖎𝖑𝖊𝖗⸗𝖕𝖑𝖆𝖙𝖊 𝖋𝖔𝖗 𝖀𝖓𝖎𝖈𝖔𝖉𝖊⸗𝕬𝖜𝖆𝖗𝖊 𝕮𝖔𝖉𝖊 🐪
Meu próprio padrão hoje em dia tende a ficar assim:
🎅 𝕹 𝖔 𝕸 𝖆 𝖎 𝖈 𝕭 𝖚 𝖑 𝖑 𝖊 𝖙.
Dizendo que “Perl deveria [de alguma forma! ] ativar o Unicode por padrão ”nem começa a pensar em dizer o suficiente para ser marginalmente útil em algum tipo de caso raro e isolado. Unicode é muito mais do que apenas um repertório de caracteres maior; é também como esses personagens interagem de muitas, muitas maneiras.
Até as medidas mínimas simplistas que (algumas) as pessoas pensam que desejam têm garantia de quebrar miseravelmente milhões de linhas de código, código que não tem chance de "atualizar" para o seu novo e admirável mundo novo corajoso moderna modernidade .
É muito mais complicado do que as pessoas fingem. Pensei muito sobre isso nos últimos anos. Eu adoraria ser mostrado que estou errado. Mas acho que não sou. O Unicode é fundamentalmente mais complexo do que o modelo que você deseja impor a ele, e aqui há complexidade que você nunca pode varrer para debaixo do tapete. Se você tentar, você quebrará seu próprio código ou o de outra pessoa. Em algum momento, você simplesmente precisa analisar e descobrir o que é o Unicode. Você não pode fingir que é algo que não é.
Faz de tudo para facilitar o Unicode, muito mais do que qualquer outra coisa que eu já usei. Se você acha que isso é ruim, tente outra coisa por um tempo. Então volte para 🐪: ou você retornará a um mundo melhor, ou trará consigo o conhecimento do mesmo para que possamos fazer uso de seu novo conhecimento para melhorar these essas coisas.
💡 𝕴𝖉𝖊𝖆𝖘 𝖋𝖔𝖗 𝖆 𝖀𝖓𝖎𝖈𝖔𝖉𝖊 𝕬𝖜𝖆𝖗𝖊 🐪 𝕷𝖆𝖚𝖓𝖉𝖗𝖞 𝕷𝖎𝖘𝖙 💡
No mínimo, aqui estão algumas coisas que parecem ser necessárias para "ativar o Unicode por padrão", como você coloca:
Todo o código fonte deve estar em UTF-8 por padrão. Você pode conseguir isso com
use utf8
ouexport PERL5OPTS=-Mutf8
.O
DATA
identificador should deve ser UTF-8. Você precisará fazer isso por pacote, como embinmode(DATA, ":encoding(UTF-8)")
.Os argumentos do programa para scripts devem ser entendidos como UTF-8 por padrão.
export PERL_UNICODE=A
, ouperl -CA
, ouexport PERL5OPTS=-CA
.Os fluxos de entrada, saída e erro padrão devem ter como padrão UTF-8.
export PERL_UNICODE=S
para todos eles, ouI
,O
e / ouE
por apenas alguns deles. Isto é comoperl -CS
.Quaisquer outros identificadores abertos por 🐪 devem ser considerados UTF-8, a menos que declarado de outra forma;
export PERL_UNICODE=D
ou comi
eo
para determinados deles;export PERL5OPTS=-CD
podia funcionar. Isso faz-CSAD
para todos eles.Cubra as duas bases e todos os fluxos que você abrir
export PERL5OPTS=-Mopen=:utf8,:std
. Veja uniquote .Você não deseja perder erros de codificação UTF-8. Tente
export PERL5OPTS=-Mwarnings=FATAL,utf8
. E certifique-se de que seus fluxos de entrada sejam semprebinmode
adequados:encoding(UTF-8)
, e não apenas adequados:utf8
.Os pontos de código entre 128 e 255 devem ser entendidos por 🐪 como os pontos de código Unicode correspondentes, e não apenas valores binários não autorizados.
use feature "unicode_strings"
ouexport PERL5OPTS=-Mfeature=unicode_strings
. Isso faráuc("\xDF") eq "SS"
e"\xE9" =~ /\w/
. Um simplesexport PERL5OPTS=-Mv5.12
ou melhor também vai conseguir isso.Os caracteres Unicode nomeados não são ativados por padrão, portanto adicione
export PERL5OPTS=-Mcharnames=:full,:short,latin,greek
ou alguns deles. Veja uninames e tcgrep .Você quase sempre precisa acessar as funções do módulo padrão de
Unicode::Normalize
vários tipos de decomposições.export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD
e, em seguida, sempre execute os itens recebidos pelo NFD e os itens de saída do NFC. Ainda não tenho uma camada de E / S para elas, mas veja nfc , nfd , nfkd e nfkc .Comparação de strings em 🐪 usando
eq
,ne
,lc
,cmp
,sort
, & c & cc estão sempre errados. Então, em vez de@a = sort @b
, você precisa@a = Unicode::Collate->new->sort(@b)
. Pode muito bem adicionar isso ao seuexport PERL5OPTS=-MUnicode::Collate
. Você pode armazenar em cache a chave para comparações binárias.🐪 built-ins gostam
printf
ewrite
fazem a coisa errada com dados Unicode. Você precisa usar oUnicode::GCString
módulo para o primeiro, e ambos, e também oUnicode::LineBreak
módulo , para o último. Veja uwc e unifmt .Se você quiser que eles contem como números inteiros, será necessário executar suas
\d+
capturas através daUnicode::UCD::num
função porque o atoi (3) interno do 🐪 não é suficientemente inteligente no momento.Você terá problemas de sistema de arquivos em sistemas de arquivos. Alguns sistemas de arquivos impõem silenciosamente uma conversão para NFC; outros silenciosamente impõem uma conversão para NFD. E outros fazem outra coisa ainda. Alguns até ignoram completamente o assunto, o que leva a problemas ainda maiores. Então você tem que fazer seu próprio manuseio de NFC / NFD para manter a sanidade.
Toda a sua 🐪 código envolvendo
a-z
ouA-Z
e tal deve ser mudado , incluindom//
,s///
etr///
. Deve destacar-se como uma bandeira vermelha gritante que seu código está quebrado. Mas não está claro como isso deve mudar. Obter as propriedades certas e entender suas dobras de caixa é mais difícil do que você imagina. Eu uso unichars e uniprops todos os dias.O código que usa
\p{Lu}
é quase tão errado quanto o código que usa[A-Za-z]
. Em\p{Upper}
vez disso, você precisa usar e saber o motivo. Sim\p{Lowercase}
e\p{Lower}
são diferentes de\p{Ll}
e\p{Lowercase_Letter}
.O código que usa
[a-zA-Z]
é ainda pior. E não pode usar\pL
ou\p{Letter}
; ele precisa usar\p{Alphabetic}
. Nem todos os alfabéticos são letras, você sabe!Se você está procurando 🐪 variáveis com
/[\$\@\%]\w+/
, então você tem um problema. Você precisa procurar/[\$\@\%]\p{IDS}\p{IDC}*/
, e mesmo isso não está pensando nas variáveis de pontuação ou nas variáveis de pacote.Se você estiver procurando por espaço em branco, deverá escolher entre
\h
e\v
, dependendo. E você nunca deve usar\s
, pois NÃO SIGNIFICA[\h\v]
, contrariamente à crença popular.Se você estiver usando
\n
para um limite de linha, ou até mesmo\r\n
, estará fazendo errado. Você tem que usar\R
, o que não é o mesmo!Se você não sabe quando e se deve chamar Unicode :: Stringprep , é melhor aprender.
As comparações que não diferenciam maiúsculas de minúsculas precisam verificar se duas coisas são as mesmas letras, independentemente de seus sinais diacríticos. A maneira mais fácil de fazer isso é com o módulo Unicode :: Collate padrão .
Unicode::Collate->new(level => 1)->cmp($a, $b)
. Existem tambémeq
métodos e tal, e você provavelmente deve aprender sobre osmatch
esubstr
métodos, também. Essas são vantagens distintas sobre os 🐪 embutidos.Às vezes isso ainda não é suficiente, e você precisa do módulo Unicode :: Collate :: Locale , como em
Unicode::Collate::Locale->new(locale => "de__phonebook", level => 1)->cmp($a, $b)
vez disso. Considere que issoUnicode::Collate::->new(level => 1)->eq("d", "ð")
é verdade, masUnicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð")
é falso. Da mesma forma, "ae" e "æ" sãoeq
se você não usa códigos de idioma ou se usa o inglês, mas eles são diferentes no código de idioma islandês. O que agora? É difícil, eu lhe digo. Você pode jogar com o ucsort para testar algumas dessas coisas.Considere como corresponder ao padrão CVCV (consoante, vogal, consoante, vogal) na sequência " niño ". Sua forma de NFD - que você se lembrava muito bem de ter colocado - se torna "nin \ x {303} o". O que você fará agora? Mesmo fingindo que uma vogal é
[aeiou]
(o que está errado, a propósito), você também não poderá fazer algo assim(?=[aeiou])\X)
, porque mesmo no NFD um ponto de código como 'ø' não se decompõe ! No entanto, o teste será igual a um 'o' usando a comparação UCA que acabei de mostrar. Você não pode confiar no NFD, mas no UCA.💩 𝔸 𝕤 𝕤 𝕦 𝕖 𝔹 𝕣 𝕠 𝕜 𝕖 𝕟 𝕟 𝕖 𝕤 💩.
E isso não é tudo. Há um milhão de suposições quebradas que as pessoas fazem sobre o Unicode. Até que eles entendam essas coisas, seu código will será quebrado.
Código que pressupõe que ele pode abrir um arquivo de texto sem especificar que a codificação está quebrada.
O código que assume que a codificação padrão é algum tipo de codificação de plataforma nativa está quebrado.
O código que assume que as páginas da Web em japonês ou chinês ocupam menos espaço no UTF-16 do que no UTF-8 está errado.
O código que assume que o Perl usa UTF-8 internamente está errado.
O código que assume que os erros de codificação sempre geram uma exceção está errado.
O código que assume pontos de código Perl limitados a 0x10_FFFF está incorreto.
O código que pressupõe que você pode definir
$/
algo que funcione com qualquer separador de linha válido está errado.O código que assume a igualdade de ida e volta na dobra de caixa, como
lc(uc($s)) eq $s
ouuc(lc($s)) eq $s
, está completamente quebrado e errado. Considere que ouc("σ")
euc("ς")
são ambos"Σ"
, maslc("Σ")
não podem retornar os dois.O código que assume que cada ponto de código em minúsculas possui um maiúsculo distinto, ou vice-versa, está quebrado. Por exemplo,
"ª"
é uma letra minúscula sem maiúscula; considerando que ambos"ᵃ"
e"ᴬ"
são letras, mas não são letras minúsculas; no entanto, ambos são pontos de código em minúsculas sem as versões em maiúsculas correspondentes. Percebido? Eles não são\p{Lowercase_Letter}
, apesar de serem ambos\p{Letter}
e\p{Lowercase}
.O código que pressupõe que a alteração de maiúsculas e minúsculas não altera o comprimento da string está quebrado.
O código que assume que há apenas dois casos está quebrado. Também há titlecase.
O código que assume apenas letras com maiúsculas e minúsculas está quebrado. Além das letras, verifica-se que números, símbolos e até marcas têm maiúsculas e minúsculas. De fato, mudar o caso pode até fazer com que algo mude sua principal categoria geral, como
\p{Mark}
transformar-se em a\p{Letter}
. Também pode mudar de um script para outro.O código que assume que o caso nunca depende de localidade é quebrado.
O código que supõe que o Unicode fornece uma informação sobre as localidades POSIX está quebrado.
O código que pressupõe que você pode remover os sinais diacríticos para obter as letras ASCII básicas é mau, imóvel, quebrado, com dano cerebral, errado e justificativa para a pena capital.
O código que assume que diacríticos
\p{Diacritic}
e marcas\p{Mark}
são a mesma coisa está quebrado.Código que assume que
\p{GC=Dash_Punctuation}
cobre tanto quanto\p{Dash}
está quebrado.O código que assume traços, hífens e desvantagens são a mesma coisa que os outros, ou que existe apenas um de cada um, está quebrado e errado.
O código que assume que todo ponto de código ocupa não mais que uma coluna de impressão está quebrado.
O código que assume que todos os
\p{Mark}
caracteres ocupam zero colunas de impressão está quebrado.Código que pressupõe que caracteres semelhantes são quebrados.
O código que assume que caracteres que não são parecidos não é semelhante está quebrado.
O código que assume que há um limite para o número de pontos de código em uma linha que apenas um
\X
pode corresponder está errado.O código que supõe que
\X
nunca pode começar com um\p{Mark}
caractere está errado.O código que assume que
\X
nunca pode conter dois\p{Mark}
caracteres não está errado.Código que assume que não pode ser usado
"\x{FFFF}"
está errado.O código que assume um ponto de código não BMP que requer duas unidades de código UTF-16 (substitutas) codificará para dois caracteres UTF-8 separados, um por unidade de código, está incorreto. Não: codifica para um ponto de código único.
O código que transcodifica de UTF ‐ 16 ou UTF ‐ 32 com listas técnicas principais para UTF ‐ 8 é quebrado se colocar uma lista técnica no início do UTF-8 resultante. Isso é tão estúpido que o engenheiro deve remover as pálpebras.
O código que assume que o CESU-8 é uma codificação UTF válida está incorreto. Da mesma forma, o código que pensa codificar U + 0000 como
"\xC0\x80"
UTF-8 está quebrado e errado. Esses caras também merecem o tratamento palpebral.O código que assume caracteres como
>
sempre aponta para a direita e<
sempre para a esquerda está errado - porque na verdade não.Código que pressupõe que, se você primeiro gera um caractere
X
e depois um caractereY
, esses serão exibidos comoXY
incorretos. Às vezes não.O código que supõe que o ASCII é bom o suficiente para escrever o inglês corretamente é estúpido, míope, analfabeto, quebrado, mal e errado. Fora com suas cabeças! Se isso parecer extremo demais, podemos comprometer: a partir de agora eles podem digitar apenas com o dedão do pé a um pé. (O restante será gravado no duto.)
O código que assume que todos
\p{Math}
os pontos de código são caracteres visíveis está errado.O código que assume que
\w
contém apenas letras, dígitos e sublinhados está incorreto.O código que assume que
^
e~
são sinais de pontuação está incorreto.Código que assume que
ü
tem um trema está errado.Código que acredita que coisas como
₨
qualquer letra contida está errado.Código que acredita
\p{InLatin}
ser o mesmo que\p{Latin}
é odiado violentamente.Código que acredita que
\p{InLatin}
quase sempre é útil é quase certamente errado.Código que acredita que, dado
$FIRST_LETTER
que é a primeira letra de algum alfabeto e$LAST_LETTER
a última letra do mesmo alfabeto, que[${FIRST_LETTER}-${LAST_LETTER}]
tem algum significado, quase sempre está completo, quebrado, errado e sem sentido.O código que acredita que o nome de alguém só pode conter certos caracteres é estúpido, ofensivo e errado.
O código que tenta reduzir Unicode para ASCII não está apenas errado, seu autor nunca deve ter permissão para trabalhar na programação novamente. Período. Eu nem tenho certeza de que eles devam ver novamente, pois obviamente não os fez muito bem até agora.
O código que acredita que existe uma maneira de fingir que as codificações de arquivos de texto não existem é quebrado e perigoso. Poderia muito bem apontar o outro olho também.
O código que converte caracteres desconhecidos em
?
está quebrado, estúpido, irracional e é contrário à recomendação padrão, que diz NÃO FAZER ISSO!RTFM por que não.O código que acredita que pode adivinhar com segurança a codificação de um arquivo de texto não marcado é culpado de uma mistura fatal de arrogância e ingenuidade que apenas um raio de Zeus corrigirá.
O código que acredita que você pode usar 🐪
printf
larguras para preencher e justificar os dados Unicode está quebrado e errado.O código que acredita que, depois de criar com êxito um arquivo com um nome determinado, que quando você executa
ls
oureaddir
em seu diretório anexo, na verdade, você encontra esse arquivo com o nome em que foi criado, com erros, interrupções e erros. Pare de se surpreender com isso!O código que acredita que o UTF-16 é uma codificação de largura fixa é estúpido, quebrado e errado. Revogue sua licença de programação.
O código que trata os pontos de código de um plano e de maneira diferente dos de qualquer outro plano é ipso facto quebrado e errado. Volte para a escola.
Código que acredita que coisas assim
/s/i
só podem corresponder"S"
ou"s"
estão quebradas e erradas. Você ficaria surpreso.O código usado
\PM\pM*
para encontrar clusters de grafema em vez de usar\X
está quebrado e errado.As pessoas que desejam voltar ao mundo ASCII devem ser incentivadas de todo o coração a fazê-lo e, em homenagem a sua atualização gloriosa, devem receber gratuitamente uma máquina de escrever manual pré-elétrica para todas as suas necessidades de entrada de dados. As mensagens enviadas a eles devem ser enviadas através de um telégrafo com 40 caracteres por linha e entregues em mão por um mensageiro. PARE.
😱 𝕾 𝖀 𝕸 𝕸 𝕽 𝖄 😱
Não sei quanto mais “Unicode padrão em 🐪” você pode obter do que escrevi. Bem, sim, sim: você deveria estar usando
Unicode::Collate
eUnicode::LineBreak
também. E provavelmente mais.Como você pode ver, há demasiadas coisas Unicode que você realmente não precisa se preocupar com para lá para sempre existe tal coisa como “padrão para Unicode”.
O que você vai descobrir, como fizemos no item 5.8, é simplesmente impossível impor todas essas coisas no código que não foi projetado desde o início para dar conta delas. Seu egoísmo bem intencionado acabou com o mundo inteiro.
E mesmo depois de fazer isso, ainda existem problemas críticos que exigem muita reflexão para serem acertados. Não há opção que você possa ativar. Nada além de cérebro, e eu quero dizer cérebro real , será suficiente aqui. Há muitas coisas que você precisa aprender. Além do retiro para a máquina de escrever manual, você simplesmente não pode esperar passar despercebido. Este é o século XXI e você não pode desejar que o Unicode se afaste por ignorância voluntária.
Você tem que aprender. Período. Nunca será tão fácil que "tudo funcione", porque isso garantirá que muitas coisas não funcionem - o que invalida a suposição de que possa haver uma maneira de "fazer tudo funcionar".
Você pode conseguir alguns padrões razoáveis para poucas e muito limitadas operações, mas não sem pensar muito mais nas coisas do que eu acho que você tem.
Como apenas um exemplo, a ordem canônica vai causar algumas dores de cabeça reais. 😭
"\x{F5}"
'õ' ,"o\x{303}"
'õ' ,"o\x{303}\x{304}"
'ȭ' e"o\x{304}\x{303}"
'ō̃' devem corresponder a 'õ' , mas como no mundo você fará isso? Isso é mais difícil do que parece, mas é algo que você precisa levar em consideração. 💣Se há uma coisa que eu sei sobre Perl, é o que seus bits Unicode fazem e não fazem, e prometo a você: “̲ᴛ̲ʜ̲ᴇ̲ʀ̲ᴇ̲ ̲ᴛ̲ʜ̲ᴇ̲ʀ̲ᴇ̲s̲ɪ̲ ̲ɴ̲ᴏ̲ ̲U̲ɴ̲ɪ̲ᴄ̲ᴏ̲ᴅ̲ᴇ̲ ̲ᴍ̲ᴀ̲ɢ̲ɪ̲ᴄ̲ ̲ʙ̲ᴜ̲ʟ̲ʟ̲ᴇ̲ᴛ̲ ̲” 😞
Você não pode simplesmente alterar alguns padrões e obter uma navegação suave. É verdade que eu corro 🐪 com
PERL_UNICODE
definido como"SA"
, mas isso é tudo, e mesmo isso é principalmente para coisas de linha de comando. Para um trabalho real, eu passo por todas as etapas descritas acima e faço muito, muito, muito cuidadosamente.Əɥ ¡ƨdləɥ ƨᴉɥʇ ədoɥ puɐ ʻλɐp əɔᴉu ɐ ʞɔnl poo⅁.
fonte
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:
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 emencode($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ãocontent
, 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 tipomy $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:E, em seguida, execute alguns dados codificados em UTF-8, como:
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 doPILE 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 :)
fonte
Encode
módulo é tedioso e propenso a erros, e torna a leitura do código referente à E / S realmente dolorosa. As camadas de E / S fornecem uma solução à medida que codificam e decodificam de forma transparente, quando necessário.open
ebinmode
permitir sua especificação, e o pragmaopen
define os padrões, como recomenda a tchrist em sua resposta.Estamos todos de acordo em que é um problema difícil por vários motivos, mas esse é precisamente o motivo de tentar facilitar a todos.
Existe um módulo recente no CPAN, utf8 :: all , que tenta "ativar o Unicode. Tudo isso".
Como foi apontado, você não pode magicamente fazer com que todo o sistema (programas externos, solicitações externas da Web etc.) também usem Unicode, mas podemos trabalhar juntos para facilitar ferramentas sensíveis que facilitam a solução de problemas comuns. Essa é a razão pela qual somos programadores.
Se utf8 :: all não fizer algo que você deveria fazer, vamos aprimorá-lo para torná-lo melhor. Ou vamos criar ferramentas adicionais que, juntas, possam atender às diferentes necessidades das pessoas, da melhor maneira possível.
`
fonte
utf8::all
módulo citado . Foi escrito antes dounicode_strings
recurso, que Fɪɴᴀʟʟʏ ᴀɴᴅ ᴀᴛ Lᴏɴɢ Lᴀsᴛ corrige regexes para ter um/u
neles. Não estou convencido de que isso gera uma exceção nos erros de codificação, e isso é algo que você realmente deve ter. Ele não carrega nouse charnames ":full"
pragma, que ainda não foi carregado automaticamente. Ele não avisa[a-z]
e tal,printf
larguras de string, usando em\n
vez de\R
e em.
vez de\X
, mas talvez essas sejam mais umaPerl::Critic
questão. Se fosse eu, adicionaria 𝐍𝐅𝐃 dentro e 𝐍𝐅𝐂 fora.unichars -gs '/(?=\P{Ll})\p{Lower}|(?=\P{Lu})\p{Upper}/x' | ucsort --upper | cat -n | less -r
. Da mesma forma, pequenas etapas de pré-processamento como... | ucsort --upper --preprocess='s/(\d+)/sprintf "%#012d", $1/ge'
essas também podem ser muito boas, e eu não gostaria de tomar decisões de outras pessoas por elas. Ainda estou construindo minha caixa de ferramentas Unicode .Eu acho que você não entendeu o Unicode e sua relação com o Perl. Não importa como você armazena dados, Unicode, ISO-8859-1 ou muitas outras coisas, seu programa precisa saber como interpretar os bytes que recebe como entrada (decodificação) e como representar as informações que deseja gerar (codificação ) Entenda errado essa interpretação e você confunde os dados. Não há nenhuma configuração mágica padrão dentro do seu programa que diga o que está fora do programa como agir.
Você acha que é difícil, provavelmente, porque você está acostumado a tudo ser ASCII. Tudo o que você deveria estar pensando era simplesmente ignorado pela linguagem de programação e por todas as coisas com as quais tinha que interagir. Se tudo não usasse nada além de UTF-8 e você não tivesse escolha, o UTF-8 seria igualmente fácil. Mas nem tudo usa UTF-8. Por exemplo, você não quer que seu identificador de entrada pense que está obtendo octetos UTF-8, a menos que seja realmente, e não deseja que seus identificadores de saída sejam UTF-8 se a coisa que ler neles puder lidar com UTF-8 . Perl não tem como saber essas coisas. É por isso que você é o programador.
Eu não acho que Unicode no Perl 5 é muito complicado. Eu acho assustador e as pessoas evitam. Há uma diferença. Para esse fim, coloquei Unicode no Learning Perl, 6ª Edição , e há muitas coisas Unicode na Effective Perl Programming . Você precisa gastar tempo para aprender e entender o Unicode e como ele funciona. Você não será capaz de usá-lo efetivamente de outra forma.
fonte
use utf8_everywhere
que as pessoas sejam felizes. Por que não o último?Ao ler este tópico, muitas vezes tenho a impressão de que as pessoas estão usando " UTF-8 " como sinônimo de " Unicode ". Faça uma distinção entre os "Pontos de código" do Unicode, que são um parente ampliado do código ASCII e as várias "codificações" do Unicode. E existem alguns deles, dos quais UTF-8, UTF-16 e UTF-32 são os atuais e outros mais são obsoletos.
Por favor, UTF-8 (assim como todas as outras codificações ) existe e tem significado apenas na entrada ou na saída. Internamente, desde o Perl 5.8.1, todas as strings são mantidas como "pontos de código" Unicode. É verdade que você precisa habilitar alguns recursos, como admiravelmente abordados anteriormente.
fonte
Existe uma quantidade realmente horrível de código antigo por aí, em grande parte, na forma de módulos CPAN comuns. Descobri que tenho que ter bastante cuidado ao ativar o Unicode se usar módulos externos que possam ser afetados por ele e ainda estou tentando identificar e corrigir algumas falhas relacionadas ao Unicode em vários scripts Perl que uso regularmente (em particular, o iTiVo falha mal em qualquer coisa que não seja ASCII de 7 bits devido a problemas de transcodificação).
fonte
-C
opção para garantir que o Perl esteja na mesma página que eu sou Unicode, porque continuo decidindo usar a ISO 8859/1 em vez do Unicode, mesmo que eu esteja explicitamente definindo$LANG
e$LC_ALL
adequadamente. (Na verdade, isso pode refletir bugs nas bibliotecas de código de idioma da plataforma.) Seja o que for, é altamente irritante que eu não possa usar o iTivo em programas com sotaques, porque os scripts Perl que executam o trabalho ficam com erros de conversão.-C
sem opções é com erros e propenso a erros . Você quebra o mundo. Defina oPERL5OPT
envariável como-C
e você verá o que quero dizer. Tentamos dessa maneira na v5.8 e foi um desastre. Você simplesmente não pode nem deve dizer aos programas que não esperam que agora estejam lidando com o Unicode, gostem ou não. Há também problemas de segurança. No mínimo, tudo o que fizerprint while <>
será interrompido se passar dados binários. O mesmo acontece com todo o código do banco de dados. Esta é uma péssima ideia.-C
sem opções. A invocação específica com a qual eu estava trabalhando era-CSDA
. Dito isto, fiquei preso com o 5.8.x por um longo tempo (olá MacPorts ...), então talvez isso tenha sido parte disso.Você deve ativar o recurso de strings unicode, e esse é o padrão se você usar a v5.14;
Você realmente não deve usar identificadores unicode esp. para código estrangeiro via utf8, pois eles são inseguros no perl5, apenas o cperl acertou. Veja, por exemplo, http://perl11.org/blog/unicode-identifiers.html
Em relação ao utf8 para seus manipuladores de arquivos / fluxos: você precisa decidir por si mesmo a codificação de seus dados externos. Uma biblioteca não pode saber disso, e como nem a libc suporta utf8, dados utf8 adequados são raros. Há mais wtf8, a aberração de janelas do utf8 ao redor.
BTW: Moose não é realmente "Modern Perl", eles apenas sequestraram o nome. Moose é perfeito perl pós-moderno no estilo Larry Wall misturado com o estilo Bjarne Stroustrup, com uma aberração eclética da sintaxe perl6 adequada, por exemplo, usando strings para nomes de variáveis, sintaxe de campos horríveis e uma implementação ingênua e imatura que é 10x mais lenta que uma implementação adequada. cperl e perl6 são os verdadeiros perls modernos, em que a forma segue a função e a implementação é reduzida e otimizada.
fonte