Unicode: isso ninguém nunca me disse…

Os fantasmas  wchar, utf-8, latin-1, et al Que volta e meia nos assombram já tiveram seus momentos nesse e em tantos outros blogs.

Estou brincando com ncurses (em python linux/windows) e vendo até onde o meu desespero de tentar codificar algo simples que possa rodar nos dois sistemas pode ir. ;-)

O caso é que me deparei, mais uma vez, com a questão dos utf-8. Os problemas surgem quando tentamos usar as bibliotecas “gringas” para escrever interfaces para usuários brasileiros. Atividade destilada de tentar fazer coisas úteis com esta montoeira de tecnologia. Básico: sempre caímos no básico. Na minha situação, o problema vem de uma confessada preguiça de estudar a fundo o assunto.

Não teve jeito… Tive que entender mais um pouco. Logo que começei a programar em python percebi que códigos não funcionavam se usássemos acentos. A solução: inserir uma meta tag no início do código

#! -*- coding: utf-8 -*- (Feio né ?! Mas no python 2.x é assim.)

Para se usar ‘,’ em vez de ponto decimal, usamos o locale no ponto onde queremos obter a tradução. (Mas na pressa, vai na mão: substitua um pelo outro na string com as funções nativas)

O que aprendi em python, depois de algumas cabeçadas é que considerarei daqui por diante em qualquer linguagem: usar unicode nessas situações.

CODIFICAÇÃO é um “conjunto de regras” que atribui VALORES NUMÉRICOS para cada caractere de texto.

ASCII

  • márcio = m\xe1rcio (representado com 6 bytes)
  • ASCII cada caractere tem 1 byte
  • ASCII só permite 128 caracteres
  • Letras acentuadas não podem ser codificadas com ASCII standard (duh!)
  • Em ASCII estendido, entretanto, criaram códigos acima de 128, para acomodar outras línguas além do inglês e gráficos em modo texto.
  • Logo o ‘á’ seria codificado com o número 0xe1, podendo variar se trocássemos a “codepage”, ou seja os números acima de 128

UTF-8

  • márcio = m\xc3\xa1rcio (representado com 7 bytes)
  • Isto é uma string de 7 bytes, 1 byte = 8 bits, 1 bit => 0/1
  • Logo este texto, utf-8, é uma string CODIFICADA (encoded) no formato UTF-8
  • Note que o ‘á’ necessita de 2 bytes para ser codificado
  • Outras codificações (i.e, outros conjuntos de regras) podem representar o’á’ usandonúmeros (a.k.a códigos) diferentes
  •  Os primeiros 128 caracteres de UTF-8 tem o mesmo codigo numérico da tabela ASCII.
  • Logo, UTF-8 é uma EXTENSÃO da tabela ASCII!

O problema: se texto funciona codificado, assim como sempre foi, por que usar o unicode?

  • Tratamento de linguagem além de inglês
  • Usar módulos de terceiros
  • Aceitar entrada de texto arbitrária. (csv, db, html, xml, etc)
  • márcio = m\u00e1rcio (len 6: não interessando com quantos bytes foi representado)

O que é o Unicode?

  • Unicode é um modo de representar texto SEM DEPENDER DE BYTES. Como é feito em UTF-8, ASCII, etc, ou seja, se ASCII, UTF-8, etc São “texto”, Unicode é “TEXTUAL”. Em menor, no Unicode TEXTO é o CONTEXTO, i.e. uma abstração para TEXTOS.
  • Um caractere unicode – chamado de Code Point – tem um NÚMERO único para cada caractere de cada linguagem
  • Incluso no Unicode, existem diversos formatos de transformação: Unicode Transformation Formats (UTF)
  • UTF-8 é um das codificações mais utilizados e 8 ali significa que números 8-bit são usados. Poderia ser 16 (UTF-16), 32 (UTF-32) etc.
  • Suporta a maioria das linguagems escritas atualmente
  • Define mais de 1 milhão de code points
  • Na prática existem limites mas na teoria, como números, que são conceitualmente diferentes de bytes, poderíamos ter infinitas representações usando números, bastaria ir ampliando a tabela.
  • Unicode é um conceito.
Letra Unicode Code Point
á \u00e1

Mas, na prática você tem que CODIFICAR em uma CODIFICAÇÃO existente, ou seja, traduzir para o código que seu sistema usa: UTF-8, latin-1, ascii, Jis, etc

Estranho, mas unicode deve ser TRANSFORMADO, quando os sistemas (legados) não trabalham com ele diretamente. No caso do python 2.x, é usada a codificação … ASCII!

O que ocorre é que quando tentamos “guardar” um caractere que “foge” dos 128 bytes da tabela “ASCII” obtemos um lindo erro:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe1 in position 2: ordinal not in range(128)

Que basicamente quer dizer que o codec unicode não consegue decifrar o código do ‘á’ usando a tabela ascii (interna), daí aquela “gambiarra” que mostrei no início ( #! -*- coding…)

Se o sistema usasse unicode – como é em python 3.x – não haveria necessidade dessas traduções pois o unicode é um conceito o invés de um “esquema” de tradução.

Tem um “maluco”, Ian Albert que compilou (e imprimiu) uma tabela gigantesca com todos os Code Points conhecidos (1.114.112 ao todo).

unichart-printed

Decodificando texto em unicode.

  • Note DECODIFICAR é traduzir o code point para o seu sistema de codificação
  • Python o faz “automágicamente”
  • A CODIFICAÇÃO padrão do python 2.x é ASCII
  • Mas esta “mágica” tem seus percalços e eventualmente geração a exceção que citei anteriormente. A do “codec”
  • Uma solução seria iniciar o módulo selecionando a tabela com “a gambiarra”, mas se fossémos rodar em um sistema “latin-1”, teríamos lindas “sopinhas de letras” na tela – Semelhante ao que o Fred citou neste artigo: ISO-8859-1? UTF-8? WINDOWS-1252?

A solução

Não tem uma solução simples e única

Temos sempre vários módulos que possuem sua própria codificação

Creio que este seja um problema que esteja ocorrendo em outras linguagens também, nunca testei, mas deduzi pois nunca entendi, de fato,  o que ocorria nestes casos. Partia para a porrada e “ajeitava” o resultado.

O sistema tem que traduzir para apresentar e “destraduzir” para armazenar.

A solução matadora seria usar todos os sitemas referenciando o unicode diretamente ( Na prática: inviável)

Para amenizar devemos então seguir os passos

1. Decodificar imeditamente, assim que encontramos a string
2. Escrever nossos códigos usando Unicode onde pudermos
3. Codificar mais tarde

O BOM… É “bom” mesmo?

Alguns formatos de arquivos usam o BOM (Byte Order Mark)

São bytes no inicio do arquivo que essencialmente querem dizer “Eu sou um UTF-8

  • Muito populares nos windozes
  • Podem ter 2, 3 ou 4 bytes

E servem para “adivinhar” a codificação do arquivo… Lógico que não dará para confiar sempre. Alguém poderia colocar ordem LSB, MSB ou fazer qualquer outra “caca” na geração dos arquivos e, como isto não é padronizado, mas sim uma “dica”, melhor ignorar.

Tipo do encoding html (Fred…) visto. Eu poderia colocar uma codificação no cabeçalho e escrever usando outra no corpo. Essas bobagens sempre ocorrem ao importar arquivos csv, planilha, dos, sqlite, firebird, etc

  • Na dúvida, “chute” UTF-8.

Conclusões

  • Unicode não é o mesmo que UTF-8.
  • O último é apenas um formato de codificação para o primeiro.
  • Se você estiver lendo codificados com UTF-8, você deve DECODIFICAR antes de usar.
  • Ao decodificar você decodifica a string codificada em UTF-8 para uma string Unicode (sim para algum código do “tabelão”)
  • Para codificar, você fazer o inverso: CODIFICA para a base que o seu sistema usa
  • Não há do que se envergonhar se não entendeu. Muitos usam regras pré definidas e aplicam, sem saber ou pensar no assunto. (Eu por exemplo)

Referências:

Tabela ASCII

Apresentação IMPORTANTE de onde partiu este poste. (Não deixe de assistir)

Stackoverflow: onde sanei a minha dúvida e me deu o link para o cerne deste POST.

Aqui você obtêm os detalhes e a imagem JPG do TABELÃO UNICODE.

 

Anúncios