Por que o Perl moderno evita o UTF-8 por padrão?

Eu me pergunto por que a maioria das soluções modernas construídas usando o Perl não habilitam o UTF-8 por padrão.

Eu entendo que há muitos problemas legados para os principais scripts Perl, onde isso pode quebrar as coisas. Mas, do meu ponto de vista, no século 21, grandes projetos novos (ou projetos com uma grande perspectiva) deveriam fazer do seu software UTF-8 a prova do zero. Ainda não vejo isso acontecendo. Por exemplo, o Moose permite rigor e avisos, mas não Unicode . Modern :: Perl reduz clichê também, mas sem manipulação UTF-8.

Por quê? Existem algumas razões para evitar o UTF-8 em projetos modernos de Perl no ano de 2011?


Comentando @tchrist ficou muito tempo, então estou adicionando aqui.

Parece que não fiquei claro. Deixe-me tentar adicionar algumas coisas.

Tchrist e eu vejo a situação de forma muito semelhante, mas nossas conclusões são completamente em lados opostos. Eu concordo, a situação com Unicode é complicada, mas é por isso que nós (usuários Perl e codificadores) precisamos de alguma camada (ou pragma) que torna o manuseio UTF-8 tão fácil quanto deve ser hoje em dia.

tchrist apontou para muitos aspectos para cobrir, vou ler e pensar sobre eles por dias ou mesmo semanas. Ainda assim, este não é o meu ponto. O tchrist tenta provar que não existe um único caminho “para habilitar o UTF-8”. Eu não tenho tanto conhecimento para discutir isso. Então, eu fico com exemplos vivos.

Eu brinquei com o Rakudo e o UTF-8 estava lá como eu precisava . Eu não tive nenhum problema, apenas funcionou. Talvez haja alguma limitação em algum lugar mais profundo, mas, no começo, tudo que testei funcionou como eu esperava.

Não deveria ser um objective no Perl 5 moderno também? Eu enfatizo mais: não estou sugerindo UTF-8 como o conjunto de caracteres padrão para o núcleo Perl, sugiro a possibilidade de ativá-lo com um piscar de olhos para aqueles que desenvolvem novos projetos.

Outro exemplo, mas com um tom mais negativo. Estruturas devem facilitar o desenvolvimento. Alguns anos atrás, eu tentei frameworks da web, mas apenas joguei fora porque “habilitar UTF-8” era tão obscuro. Eu não encontrei como e onde ligar suporte Unicode. Foi tão demorado que achei mais fácil seguir o caminho antigo. Agora eu vi aqui houve uma recompensa para lidar com o mesmo problema com Mason 2: Como fazer Mason2 UTF-8 limpo? . Então, é um framework bastante novo, mas usá-lo com o UTF-8 precisa de um profundo conhecimento de seus internos. É como um grande sinal vermelho: PARE, não me use!

Eu gosto muito do Perl. Mas lidar com Unicode é doloroso. Eu ainda me vejo correndo contra as paredes. De certa forma, tchrist está certo e responde às minhas perguntas: novos projetos não atraem UTF-8 porque é muito complicado em Perl 5.


🌴 🐪🐫🐪🐫🐪 🌞 𝕲𝖔 𝕿𝖍𝖔𝖚 𝖆𝖓𝖉 𝕯𝖔 𝕷𝖎𝖐𝖊𝖜𝖎𝖘𝖊 🌞 🐪🐫🐪 🐁


𝙎𝙞𝙢𝙥𝙡𝙚𝙨𝙩: 𝙎𝙞𝙢𝙥𝙡𝙚𝙨𝙩 : 𝟕 𝘿𝙞𝙨𝙘𝙧𝙚𝙩𝙚 𝙍𝙚𝙘𝙤𝙢𝙢𝙚𝙣𝙙𝙖𝙩𝙞𝙤𝙣𝙨

  1. Defina sua PERL_UNICODE PERL_UNICODE como AS . Isso faz com que todos os scripts Perl decodifiquem @ARGV como cadeias de caracteres UTF-8 e defina a codificação de todos os três stdin, stdout e stderr como UTF-8. Ambos são efeitos globais, não lexicais.

  2. No topo do seu arquivo fonte (programa, módulo, biblioteca, do hickey), proeminentemente declare que você está executando o perl versão 5.12 ou melhor via:

    use v5.12; # minimal for unicode string feature

    use v5.14; # optimal for unicode string feature

  3. Ative avisos, pois a declaração anterior permite apenas restrições e resources, não avisos. Sugiro também a promoção de avisos Unicode em exceções, portanto, use essas duas linhas, não apenas uma delas. Observe, entretanto, que sob a v5.14, a class de aviso utf8 compreende três outros sub-avisos que podem ser ativados separadamente: nonchar , surrogate e non_unicode . Estes você pode querer exercer maior controle sobre.

    use warnings;

    use warnings qw( FATAL utf8 );

  4. Declare que esta unidade de origem está codificada como UTF-8. Embora uma vez esse pragma tenha feito outras coisas, ele agora serve a esse único propósito singular e não a outro:

    use utf8;

  5. Declare que qualquer coisa que abra um filehandles dentro deste escopo léxico, mas não em outro lugar, é assumir que esse stream é codificado em UTF-8, a menos que você diga o contrário. Dessa forma, você não afeta o código de outro módulo ou de outro programa.

    use open qw( :encoding(UTF-8) :std );

  6. Ative os caracteres nomeados por meio de \N{CHARNAME} .

    use charnames qw( :full :short );

  7. Se você tiver um identificador de DATA , você deve definir explicitamente sua codificação. Se você quiser que isso seja UTF-8, diga:

    binmode(DATA, ":encoding(UTF-8)");

Obviamente, não há fim para outros assuntos com os quais você possa acabar se preocupando, mas isso será suficiente para aproximar a meta do Estado de “fazer tudo funcionar com o UTF-8”, embora para um sentido um pouco enfraquecido desses termos.

Um outro pragma, embora não seja relacionado ao Unicode, é:

  use autodie; 

É altamente recomendável.


🎅 𝕹 𝖔 𝕸 𝖆 𝖌 𝖎 𝖎 𝖈 𝕭 𝖚 𝖑 𝖑 𝖊 𝖙 🎅


Dizendo que “Perl deveria [de alguma forma! ] habilitar Unicode por padrão ”nem começa a pensar em dizer o suficiente para ser até mesmo marginalmente útil em algum tipo de caso raro e isolado. O Unicode é muito mais do que apenas um repertório de caracteres maior; É também como esses personagens interagem de muitas maneiras.

Mesmo as medidas mínimas simplistas que (algumas) pessoas parecem achar que querem, garantidamente quebram miseravelmente milhões de linhas de código, código que não tem chance de “atualizar” para sua nova e elegante modernidade.

É muito mais complicado do que as pessoas fingem. Eu pensei sobre isso muito, muito nos últimos anos. Eu adoraria ser mostrado que estou errado. Mas eu não acho que sou. Unicode é fundamentalmente mais complexo do que o modelo que você gostaria de impor, e há complexidade aqui que você nunca pode varrer para baixo do tapete. Se você tentar, você quebrará seu próprio código ou o de outra pessoa. Em algum momento, você simplesmente tem que quebrar e aprender o que é Unicode. Você não pode fingir que é algo que não é.

🐪 faz de tudo para tornar o Unicode mais fácil, muito mais do que qualquer outra coisa que já usei. Se você acha que isso é ruim, tente outra coisa por um tempo. Então volte para 🐪: ou você terá retornado a um mundo melhor, ou então você trará conhecimento do mesmo com você para que possamos fazer uso de seu novo conhecimento para se tornar melhor nessas coisas.


💡 𝕴𝖉𝖊𝖆𝖘 𝖋𝖔𝖗 𝖆 𝖀𝖓𝖎𝖈𝖔𝖉𝖊 ⸗ 𝕬𝖜𝖆𝖗𝖊 🐪 𝕷𝖆𝖚𝖓𝖉𝖗𝖞 𝕷𝖎𝖘𝖙 💡


No mínimo, aqui estão algumas coisas que parecem ser necessárias para que enable habilite o Unicode por padrão, como você diz:

  1. Todo código-fonte deve estar em UTF-8 por padrão. Você pode obter isso use utf8 ou export PERL5OPTS=-Mutf8 .

  2. O identificador 🐪 DATA deve ser UTF-8. Você terá que fazer isso por pacote, como em binmode(DATA, ":encoding(UTF-8)") .

  3. Argumentos do programa para scripts devem ser entendidos como UTF-8 por padrão. export PERL_UNICODE=A ou perl -CA ou export PERL5OPTS=-CA .

  4. Os streams padrão de input, saída e erro devem ser padronizados como UTF-8. export PERL_UNICODE=S para todos eles, ou I , O , e / ou E para apenas alguns deles. Isso é como perl -CS .

  5. Quaisquer outras alças abertas por 🐪 devem ser consideradas UTF-8, a menos que seja declarado o contrário; export PERL_UNICODE=D ou com i e o para determinados destes; export PERL5OPTS=-CD funcionaria. Isso faz -CSAD para todos eles.

  6. Cubra as duas bases mais todos os streams abertos com a export PERL5OPTS=-Mopen=:utf8,:std . Veja uniquote .

  7. Você não quer perder erros de codificação UTF-8. Tente export PERL5OPTS=-Mwarnings=FATAL,utf8 . E certifique-se de que seus streams de input estejam sempre binmode d para :encoding(UTF-8) , não apenas para :utf8 .

  8. Os pontos de código entre 128 e 255 devem ser entendidos por 🐪 como sendo os pontos de código Unicode correspondentes, não apenas valores binários não propostos. use feature "unicode_strings" ou export PERL5OPTS=-Mfeature=unicode_strings . Isso fará com que uc("\xDF") eq "SS" e "\xE9" =~ /\w/ . Uma export PERL5OPTS=-Mv5.12 simples export PERL5OPTS=-Mv5.12 ou melhor também irá receber isso.

  9. Caracteres Unicode nomeados não são ativados por padrão, portanto, adicione export PERL5OPTS=-Mcharnames=:full,:short,latin,greek ou algo export PERL5OPTS=-Mcharnames=:full,:short,latin,greek . Veja uninames e tggrep .

  10. Você quase sempre precisa acessar as funções do módulo padrão Unicode::Normalize vários tipos de decomposições. export PERL5OPTS=-MUnicode::Normalize=NFD,NFKD,NFC,NFKD e, em seguida, sempre executa input de material através de NFD e material de saída de NFC. Não há nenhuma camada de E / S para essas ainda que eu saiba, mas veja nfc , nfd , nfkd e nfkc .

  11. Comparações de strings em 🐪 usando eq , ne , lc , cmp , sort , & c & cc estão sempre erradas. Então, ao invés de @a = sort @b , você precisa de @a = Unicode::Collate->new->sort(@b) . Pode também adicionar isso à sua export PERL5OPTS=-MUnicode::Collate . Você pode armazenar em cache a chave para comparações binárias.

  12. 🐪 built-ins como printf e write fazem a coisa errada com dados Unicode. Você precisa usar o módulo Unicode::GCString para o primeiro, e tanto o módulo Unicode::LineBreak quanto o último. Veja uwc e unifmt .

  13. Se você quiser que eles sejam contados como inteiros, então você terá que executar suas capturas de \d+ através da function Unicode::UCD::num porque o atoi (3) interno de isn’t não é atualmente bastante inteligente.

  14. Você terá problemas no sistema de arquivos em filesystems. Alguns filesystems reforçam silenciosamente uma conversão para NFC; outros silenciosamente reforçam uma conversão para NFD. E outros ainda fazem outra coisa. Alguns até ignoram completamente o assunto, o que leva a problemas ainda maiores. Então você tem que fazer o seu próprio manuseio de NFC / NFD para manter sã.

  15. Todo o seu código envolvendo az ou AZ deve ser MUDADO , incluindo m// , s/// e tr/// . Deve se destacar como uma bandeira vermelha gritando que seu código está quebrado. Mas não está claro como isso deve mudar. Obtendo as propriedades corretas e entendendo seus casefolds, é mais difícil do que você imagina. Eu uso unichars e uniprops todos os dias.

  16. Código que usa \p{Lu} é quase tão errado quanto código que usa [A-Za-z] . Você precisa usar \p{Upper} , e sabe o motivo. Sim, \p{Lowercase} e \p{Lower} são diferentes de \p{Ll} e \p{Lowercase_Letter} .

  17. 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!

  18. Se você está procurando por variables ​​com /[\$\@\%]\w+/ , então você tem um problema. Você precisa procurar por /[\$\@\%]\p{IDS}\p{IDC}*/ , e mesmo isso não está pensando nas variables ​​de pontuação ou variables ​​de pacote.

  19. Se você está verificando espaços em branco, então você deve escolher entre \h \v , dependendo. E você nunca deve usar \s , já que NÃO SIGNIFICA [\h\v] , ao contrário da crença popular.

  20. Se você estiver usando \n para um limite de linha, ou mesmo \r\n , então você está fazendo errado. Você tem que usar o \R , que não é o mesmo!

  21. Se você não sabe quando e se deve chamar Unicode :: Stringprep , então é melhor aprender.

  22. 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 diacríticos e tal. A maneira mais fácil de fazer isso é com o módulo padrão Unicode :: Collate . Unicode::Collate->new(level => 1)->cmp($a, $b) . Há também methods eq e tal, e você provavelmente deve aprender sobre os methods match e substr também. Estes têm vantagens distintas sobre os 🐪 built-ins.

  23. À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 Unicode::Collate::->new(level => 1)->eq("d", "ð") é verdadeiro, mas Unicode::Collate::Locale->new(locale=>"is",level => 1)->eq("d", " ð") é falso. Da mesma forma, “ae” e “æ” são eq se você não usa locales, ou se você usa o idioma inglês, mas eles são diferentes na localidade islandesa. O que agora? É difícil, eu te digo. Você pode jogar com o ucsort para testar algumas dessas coisas.

  24. Considere como combinar o padrão CVCV (consoante, vogal, consoante, vogal) na string “ niño ”. Sua forma NFD – que você tinha melhor bem lembrado de colocá-lo – torna-se “nin \ x {303} o”. O que você fará agora? Mesmo fingindo que uma vogal é [aeiou] (o que é errado, a propósito), você não será capaz de fazer algo como (?=[aeiou])\X) , porque mesmo em NFD um ponto de código como ‘ ø ‘ não se decompõe ! No entanto, ele testará igual a um ‘o’ usando a comparação UCA que acabei de mostrar. Você não pode confiar no NFD, você tem que confiar no UCA.


💩 𝔸 𝕤 𝕤 𝕦 𝕞 𝕖 𝔹 𝔹 𝕣 𝕠 𝕠 𝕜 𝕖 𝕟 𝕟 𝕖 𝕤 𝕤 💩


E isso não é tudo. Há milhões de suposições quebradas que as pessoas fazem sobre o Unicode. Até que eles entendam essas coisas, o código deles será quebrado.

  1. Código que assume que pode abrir um arquivo de texto sem especificar que a codificação está quebrada.

  2. Código que assume a codificação padrão é algum tipo de codificação de plataforma nativa é quebrada.

  3. Código que assume que páginas da web em japonês ou chinês ocupam menos espaço em UTF-16 do que em UTF-8 está errado.

  4. O código que assume que o Perl usa o UTF-8 internamente está errado.

  5. Código que assume que os erros de codificação sempre levantam uma exceção está errado.

  6. Código que assume pontos de código Perl são limitados a 0x10_FFFF está errado.

  7. Código que assume que você pode definir $/ para algo que funcionará com qualquer separador de linha válido está errado.

  8. Código que assume igualdade de roundtrip no casefolding, como lc(uc($s)) eq $s ou uc(lc($s)) eq $s , está completamente quebrado e errado. Considere que o uc("σ") e uc("ς") são ambos "Σ" , mas lc("Σ") não pode retornar ambos.

  9. Código que assume que cada ponto de código minúsculo tem um maiúsculo distinto, ou vice-versa, é quebrado. Por exemplo, "ª" é uma letra minúscula sem letras maiúsculas; enquanto que tanto "ᵃ" como "ᴬ" são letras, mas não são letras minúsculas; no entanto, eles são ambos os pontos de código minúsculos sem correspondentes versões maiúsculas. Percebido? Eles não são \p{Lowercase_Letter} , apesar de serem \p{Letter} e \p{Lowercase} .

  10. Código que assume a mudança do caso não muda o comprimento da string está quebrado.

  11. Código que assume que há apenas dois casos quebrados. Há também titlecase.

  12. Código que assume apenas letras caso esteja quebrado. Além de apenas letras, verifica-se que números, símbolos e até mesmo marcas têm o caso. De fato, mudar o caso pode fazer com que algo mude sua categoria geral principal, como \p{Mark} transformando em \p{Letter} . Ele também pode fazer com que ele mude de um script para outro.

  13. Código que assume que o caso nunca é dependente de localidade está quebrado.

  14. Código que assume Unicode dá uma idéia sobre locales POSIX está quebrado.

  15. Código que assume que você pode remover diacríticos para obter letras ASCII básicas é mal, ainda, quebrado, danificado pelo cérebro, errado e justificativa para a pena capital.

  16. Código que assume que diacríticos \p{Diacritic} e marcas \p{Mark} são a mesma coisa que está quebrada.

  17. Código que pressupõe que \p{GC=Dash_Punctuation} cobre tanto quanto \p{Dash} está corrompido.

  18. Código que assume traços, hífens e minuses são a mesma coisa um do outro, ou que existe apenas um de cada, está quebrado e errado.

  19. Código que assume que todo ponto de código ocupa não mais do que uma coluna de impressão está quebrada.

  20. Código que assume que todos os caracteres \p{Mark} ocupam zero de colunas de impressão está quebrado.

  21. Código que pressupõe que caracteres parecidos são iguais.

  22. Código que assume que os caracteres que não são parecidos não são iguais.

  23. 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.

  24. Código que assume \X nunca pode começar com um caractere \p{Mark} está errado.

  25. Código que assume que \X nunca pode conter dois caracteres que não sejam \p{Mark} está errado.

  26. Código que assume que não pode usar "\x{FFFF}" está errado.

  27. 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á errado. Não faz: codifica para um único ponto de código.

  28. O código que transcodifica de UTF-16 ou UTF-32 com BOMs principais em UTF-8 é interrompido se colocar uma BOM no início do UTF-8 resultante. Isso é tão estúpido que o engenheiro deve ter suas pálpebras removidas.

  29. Código que assume que o CESU-8 é uma codificação UTF válida está errada. 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 das pálpebras.

  30. Código que assume caracteres como > sempre aponta para a direita e < sempre aponta para a esquerda estão errados - porque na verdade não.

  31. Código que assume se você produz primeiro o caractere X e depois o caractere Y , que eles aparecerão como XY está errado. Às vezes eles não.

  32. Código que assume que ASCII é bom o suficiente para escrever Inglês corretamente é estúpido, míope, analfabeto, quebrado, mal e errado. Fora com suas cabeças! Se isso parece extremo demais, podemos nos comprometer: a partir daí, eles só poderão digitar com o dedão do pé de um pé (o resto ainda será tapado).

  33. Código que assume que todos os pontos de código \p{Math} são caracteres visíveis está errado.

  34. Código que assume \w contém apenas letras, dígitos e sublinhados está errado.

  35. Código que assume que ^ e ~ são sinais de pontuação está errado.

  36. Código que assume que ü tem um trema está errado.

  37. Código que acredita que coisas como contêm quaisquer letras nelas estão erradas.

  38. O código que acredita que \p{InLatin} é o mesmo que \p{Latin} está terrivelmente quebrado.

  39. O código que acredita que \p{InLatin} é quase sempre útil é quase certamente errado.

  40. Código que acredita que, dado que $FIRST_LETTER como a primeira letra de um alfabeto e $LAST_LETTER como a última letra do mesmo alfabeto, que [${FIRST_LETTER}-${LAST_LETTER}] tem algum significado, quase sempre é completo, quebrado e errado sem significado.

  41. O código que acredita que o nome de alguém pode conter apenas determinados caracteres é estúpido, ofensivo e errado.

  42. Código que tenta reduzir Unicode para ASCII não é meramente errado, seu perpetrador nunca deve ter permissão para trabalhar na programação novamente. Período. Eu nem tenho certeza de que eles deveriam ter permissão para ver novamente, já que obviamente não os fez muito bem até agora.

  43. Código que acredita que há alguma maneira de fingir que codificações de arquivos de texto não existem está quebrado e é perigoso. Pode também cutucar o outro olho também.

  44. Código que converte caracteres desconhecidos para ? está quebrado, estúpido, braindead e contraria a recomendação padrão, que diz NÃO FAZER ISSO! RTFM por que não.

  45. Código que acredita poder adivinhar de forma confiável a codificação de um arquivo de texto não marcado é culpado de uma mistura fatal de arrogância e ingenuidade que somente um relâmpago do Zeus consertará.

  46. O código que acredita que você pode usar 🐪 printf widths para preencher e justificar que os dados Unicode estão corrompidos e errados.

  47. Código que acredita que, uma vez que você tenha criado com sucesso um arquivo com um determinado nome, quando executar ls ou readdir no diretório que o readdir , você realmente descobrirá que o arquivo com o nome em que o criou está cheio de bugs, quebrado e errado. Pare de ser surpreendido por isso!

  48. O código que acredita que o UTF-16 é uma codificação de largura fixa é estúpido, quebrado e errado. Revogar sua licença de programação.

  49. O código que trata os pontos de código de um plano um pouco diferente dos de qualquer outro plano é ipso facto quebrado e errado. Volte para a escola.

  50. Código que acredita que coisas como /s/i só podem combinar "S" ou "s" estão quebradas e erradas. Você ficaria surpreso.

  51. Código que usa \PM\pM* para encontrar clusters de grafema em vez de usar \X está quebrado e errado.

  52. As pessoas que querem voltar para o mundo ASCII devem ser encorajadas a fazê-lo, e em homenagem à sua gloriosa atualização, elas devem ser fornecidas gratuitamente com uma máquina de escrever manual pré-elétrica para todas as suas necessidades de input de dados. As mensagens enviadas para eles devem ser enviadas através de um telégrafo de 40 caracteres por linha e entregues manualmente por um mensageiro. PARE.


🎁 🐪 𝕭𝖔𝖎𝖑𝖊𝖗⸗𝖕𝖑𝖆𝖙𝖊 𝖋𝖔𝖗 𝖀𝖓𝖎𝖈𝖔𝖉𝖊⸗𝕬𝖜𝖆𝖗𝖊 𝕮𝖔𝖉𝖊 🐪 🎁


Meu próprio clichê nos dias de hoje tende a se parecer com isso:

 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__ 

😱 𝕾 𝖀 𝕸 𝕸 𝕬 𝕽 𝖄 😱


Eu não sei o quanto mais "default Unicode in" você pode obter do que eu escrevi. Bem, sim eu faço: você deve usar Unicode::Collate e Unicode::LineBreak também. E provavelmente mais.

Como você pode ver, há muitas coisas em Unicode com as quais você realmente precisa se preocupar para que exista algo como "padrão para Unicode".

O que você vai descobrir, assim como fizemos em 5.8, é simplesmente impossível impor todas essas coisas em códigos que não foram projetados desde o início para explicá-los. Seu egoísmo bem intencionado acabou com o mundo inteiro.

E mesmo quando você faz, ainda há questões críticas que exigem uma grande dose de pensamento para acertar. Não há nenhum interruptor que você possa virar. Nada além de cérebro, e quero dizer cérebro real , será suficiente aqui. Há um monte de coisas que você tem que aprender. Modulo a retirada para a máquina de escrever manual, você simplesmente não pode esperar se esgueirar pela ignorância. Este é o século 21, e você não pode desejar Unicode embora por ignorância intencional.

Você tem que aprender. Período. Nunca será tão fácil que “tudo simplesmente funcione”, porque isso garantirá que muitas coisas não funcionam - o que invalida a suposição de que pode haver uma maneira de “fazer tudo funcionar”.

Você pode conseguir alguns padrões razoáveis ​​para operações muito poucas e muito limitadas, mas não sem pensar em coisas muito mais do que eu acho que você tem.

Como apenas um exemplo, o ordenamento canônico causará algumas dores de cabeça reais. "\x{F5}" 'õ' , "o\x{303}" 'õ' , "o\x{303}\x{304}" 'ȭ' e "o\x{304}\x{303}" 'ō̃' deve ser igual a 'õ' , mas como no mundo você vai fazer isso? Isso é mais difícil do que parece, mas é algo que você precisa considerar. 💣

Se há uma coisa que eu sei sobre Perl, é o que seus bits Unicode fazem e não fazem, e isso prometo a você: “̲ᴛ̲ʜ̲ᴇ̲ʀ̲ᴇ̲ ̲ɪ̲ ̲ ̲ ̲ ̲ɴ̲ɪ̲ᴄ̲ᴏ̲ᴅ̲ᴇ̲ ̲ɴ̲ɪ̲ᴄ̲ᴏ̲ᴅ̲ᴇ̲ ̲ɴ̲ɪ̲ᴄ̲ᴏ̲ᴅ̲ᴇ̲ ̲ ̲” 😞

Você não pode simplesmente alterar alguns padrões e navegar tranquilamente. É verdade que eu corro com o PERL_UNICODE definido como "SA" , mas isso é tudo, e mesmo isso é principalmente para coisas de linha de comando. Para o trabalho real, passo por todos os passos descritos acima, e faço muito, muito ** cuidadosamente.


😈 ƨ əɥ əɥ əɥ ə ɥ ɥ ɥ ɥ ɥ l ʞɔ ʞɔ ʞɔ ʞɔ ʞɔ ʞɔ ʞɔ ʞɔ l

Existem dois estágios para processar o texto Unicode. O primeiro é “como posso inseri-lo e enviá-lo sem perder informações”. A segunda é “como tratar o texto de acordo com as convenções de idioma local”.

O post de tchrist cobre ambos, mas a segunda parte é de onde vem 99% do texto em seu post. A maioria dos programas nem sequer lida com E / S corretamente, por isso é importante entender que antes mesmo de começar a se preocupar com a normalização e o agrupamento.

Este post visa resolver esse primeiro problema

Quando você lê dados no Perl, não importa qual é a codificação. Aloca alguma memory e armazena os bytes lá fora. Se você disser print $str , ele simplesmente apagará esses bytes para o seu terminal, o que provavelmente é definido para assumir que tudo que está escrito nele é UTF-8, e seu texto aparece.

Maravilhoso.

Exceto, não é. Se você tentar tratar os dados como texto, verá que algo ruim está acontecendo. Você precisa ir além da length para ver que o que o Perl pensa sobre a sua string e o que você acha da sua string não está de acordo. Escreva um one-liner como: perl -E 'while(<>){ chomp; say length }' perl -E 'while(<>){ chomp; say length }' e digite 文字化け e você terá 12 … não a resposta correta, 4.

Isso porque o Perl assume que sua string não é texto. Você tem que dizer que é um texto antes que ele lhe dê a resposta certa.

Isso é bastante fácil; o módulo Encode tem as funções para fazer isso. O ponto de input genérico é Encode::decode (ou use Encode qw(decode) , é claro). Essa function pega alguma string do mundo externo (o que nós chamamos de “octetos”, uma maneira de dizer “bytes de 8 bits”), e a transforma em algum texto que 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 contendo o texto.

(Há também Encode::decode_utf8 , que assume o UTF-8 para a codificação.)

Se nós rewritemos nosso one-liner:

 perl -MEncode=decode -E 'while(<>){ chomp; say length decode("UTF-8", $_) }' 

Nós digitamos 文字 化 け e obtemos “4” como resultado. Sucesso.

Isso, ali mesmo, é a solução para 99% dos problemas Unicode em Perl.

A chave é, sempre que algum texto entra em seu programa, você deve decodificá-lo. A Internet não pode transmitir caracteres. Os arquivos não podem armazenar caracteres. Não há caracteres em seu database. Existem apenas octetos e você não pode tratar os octetos como caracteres em 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; basta dizer use Encode qw(encode) , decidir em qual codificação seus dados estarão (UTF-8 para terminais que entendem UTF-8, UTF-16 para arquivos no Windows, etc.) e, em seguida, gerar o resultado da encode($encoding, $data) vez de apenas enviar $data .

Esta operação converte os caracteres de Perl, que é o que seu programa opera, em octetos que podem ser usados ​​pelo mundo externo. Seria muito mais fácil se pudéssemos enviar caracteres pela Internet ou para nossos terminais, mas não podemos: apenas octetos. Então temos que converter caracteres em octetos, caso contrário os resultados são indefinidos.

Para resumir: codifique todas as saídas e decodifique todas as inputs.

Agora vamos falar sobre três questões que tornam isso um pouco desafiador. O primeiro é bibliotecas. Eles manipulam o texto corretamente? A resposta é … eles tentam. Se você fizer o download de uma página da web, o LWP retornará seu resultado como texto. Se você chamar o método correto no resultado, isso é (e isso acontece de ser decoded_content , não content , que é apenas o stream de octetos que ele recebeu do servidor). Os drivers de database podem ser escamosos; se você usar DBD :: SQLite com apenas Perl, ele funcionará, mas se alguma outra ferramenta colocar o texto armazenado como alguma codificação diferente de UTF-8 em seu database … bem … ele não será tratado corretamente até você escrever o código para manipulá-lo corretamente.

Em geral, a saída de dados é mais fácil, mas se você vir “caractere largo na impressão”, sabe que está bagunçando 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 sentido”. Seu programa parece funcionar (porque a outra extremidade normalmente lida com os caracteres Perl brutos corretamente), mas está muito quebrada e pode parar de funcionar a qualquer momento. Corrigi-lo com um Encode::encode explícito!

O segundo problema é o código-fonte codificado em UTF-8. A menos que você diga use utf8 no topo de cada arquivo, Perl não assumirá que seu código-fonte é UTF-8. Isto significa que cada vez que você diz algo como my $var = 'ほげ' , você está injetando lixo em seu programa que irá quebrar tudo de uma forma horrível. Você não precisa “usar utf8”, mas se não tiver, não deve usar nenhum caractere não-ASCII em seu programa.

O terceiro problema é como Perl lida com o passado. Há muito tempo atrás, não existia Unicode e Perl supunha que tudo era texto ou binário latino-1. Assim, quando os dados chegam ao 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 pedimos o comprimento de “文字 化 け”, obtivemos 12. Perl assumiu 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ê quer se o seu texto não for Latin-1. É por isso que é crítico decodificar explicitamente a input: se você não fizer isso, o Perl fará, e poderá fazer errado.

As pessoas enfrentam problemas onde metade dos dados são uma sequência de caracteres adequada e alguns ainda são binários. Perl irá interpretar a parte que ainda é binária como se fosse texto Latin-1 e então combiná-la com os dados de caractere corretos. Isso fará com que pareça que manipular seus personagens corretamente quebrou seu programa, mas na realidade, você simplesmente não consertou o suficiente.

Aqui está um exemplo: você tem um programa que lê um arquivo de texto codificado em UTF-8, você cola em uma PILE OF POO Unicode em cada linha e imprime. Você escreve assim:

 while(<>){ chomp; say "$_ 💩"; } 

Em seguida, execute alguns dados codificados em UTF-8, como:

 perl poo.pl input-data.txt 

Imprime os dados UTF-8 com um cocô no final de cada linha. Perfeito, meu programa funciona!

Mas não, você está apenas fazendo a concatenação binária. Você está lendo octetos a partir do arquivo, removendo um \n com chomp e, em seguida, adicionando os bytes na representação UTF-8 do caractere PILE OF POO . Quando você revisa seu programa para decodificar os dados do arquivo e codifica a saída, você notará que obtém lixo (“ð ©”) ao invés do cocô. Isso levará você a acreditar que a decodificação do arquivo de input é a coisa errada a fazer. Não é.

O problema é que o cocô está sendo implicitamente atualizado como latin-1. Se você use utf8 para fazer o texto literal em vez de binário, ele funcionará novamente!

(Esse é o problema número um que vejo quando estou ajudando pessoas com Unicode. Eles fizeram parte corretamente e quebraram o programa. Isso é o que é triste com resultados indefinidos: você pode ter um programa funcionando por um longo tempo, mas quando você começa a consertá-lo, Não se preocupe, se você está adicionando instruções de codificação / decodificação ao seu programa e ele quebra, significa apenas que você tem mais trabalho a fazer. Da próxima vez, quando você projetar com o Unicode em mente desde o início, ele será muito facil!)

Isso é tudo que você precisa saber sobre Perl e Unicode. Se você disser ao Perl quais são seus dados, ele terá o melhor suporte a Unicode entre todas as linguagens de programação populares. Se você assumir que, magicamente, saberá que tipo de texto você está alimentando, então, você vai estragar seus dados irrevogavelmente. Só porque o seu programa funciona hoje em seu terminal UTF-8 não significa que ele funcionará amanhã em um arquivo codificado UTF-16. Então, faça-o seguro agora e salve-se da dor de cabeça de destruir os dados dos seus usuários!

A parte fácil de manipular Unicode é codificar a input de saída e decodificação. A parte difícil é encontrar todas as suas inputs e saídas e determinar qual delas é. Mas é por isso que você ganha muito dinheiro 🙂

Estamos todos de acordo que é um problema difícil por muitas razões, mas essa é precisamente a razão para tentar tornar isso mais fácil para todos.

Existe um módulo recente no CPAN, utf8 :: all , que tenta “ativar Unicode. Tudo isso”.

Como já foi dito, você não pode magicamente fazer com que todo o sistema (programas externos, solicitações externas da Web, etc.) usem Unicode também, mas podemos trabalhar juntos para criar ferramentas sensatas que facilitam a realização de problemas comuns. Essa é a razão pela qual somos programadores.

Se utf8 :: all não fizer algo que você acha que deveria, vamos melhorar para melhorar. Or let’s make additional tools that together can suit people’s varying needs as well as possible.

`

I think you misunderstand Unicode and its relationship to Perl. No matter which way you store data, Unicode, ISO-8859-1 , or many other things, your program has to know how to interpret the bytes it gets as input (decoding) and how to represent the information it wants to output (encoding). Get that interpretation wrong and you garble the data. There isn’t some magic default setup inside your program that’s going to tell the stuff outside your program how to act.

You think it’s hard, most likely, because you are used to everything being ASCII. Everything you should have been thinking about was simply ignored by the programming language and all of the things it had to interact with. If everything used nothing but UTF-8 and you had no choice, then UTF-8 would be just as easy. But not everything does use UTF-8. For instance, you don’t want your input handle to think that it’s getting UTF-8 octets unless it actually is, and you don’t want your output handles to be UTF-8 if the thing reading from them can handle UTF-8. Perl has no way to know those things. That’s why you are the programmer.

I don’t think Unicode in Perl 5 is too complicated. I think it’s scary and people avoid it. There’s a difference. To that end, I’ve put Unicode in Learning Perl, 6th Edition , and there’s a lot of Unicode stuff in Effective Perl Programming . You have to spend the time to learn and understand Unicode and how it works. You’re not going to be able to use it effectively otherwise.

While reading this thread, I often get the impression that people are using ” UTF-8 ” as a synonym to ” Unicode “. Please make a distinction between Unicode’s “Code-Points” which are an enlarged relative of the ASCII code and Unicode’s various “encodings”. And there are a few of them, of which UTF-8, UTF-16 and UTF-32 are the current ones and a few more are obsolete.

Please, UTF-8 (as well as all other encodings ) exists and have meaning in input or in output only. Internally, since Perl 5.8.1, all strings are kept as Unicode “Code-points”. True, you have to enable some features as admiringly covered previously.

There’s a truly horrifying amount of ancient code out there in the wild, much of it in the form of common CPAN modules. I’ve found I have to be fairly careful enabling Unicode if I use external modules that might be affected by it, and am still trying to identify and fix some Unicode-related failures in several Perl scripts I use regularly (in particular, iTiVo fails badly on anything that’s not 7-bit ASCII due to transcoding issues).

You should enable the unicode strings feature, and this is the default if you use v5.14;

You should not really use unicode identifiers esp. for foreign code via utf8 as they are insecure in perl5, only cperl got that right. See eg http://perl11.org/blog/unicode-identifiers.html

Regarding utf8 for your filehandles/streams: You need decide by yourself the encoding of your external data. A library cannot know that, and since not even libc supports utf8, proper utf8 data is rare. There’s more wtf8, the windows aberration of utf8 around.

BTW: Moose is not really “Modern Perl”, they just hijacked the name. Moose is perfect Larry Wall-style postmodern perl mixed with Bjarne Stroustrup-style everything goes, with an eclectic aberration of proper perl6 syntax, eg using strings for variable names, horrible fields syntax, and a very immature naive implementation which is 10x slower than a proper implementation. cperl and perl6 are the true modern perls, where form follows function, and the implementation is reduced and optimized.