domingo, 29 de julho de 2012

Por que UTF-8 e não ASCII para o Português? (PARTE I)

Um outro post que fiz na Python-Brasil:

Os colegas já falaram sobre o por quê do UTF-8.

Eu gostaria apenas de lembrar que o assunto é mais complicado do que parece, por exemplo no Python 2.7:
# -*- coding: utf-8 -*-
print "Acentos: áéíóúãõç"
print u"Acentos2: áéíóúãõç"


Execute o programa acima no Windows, pode ser pelo IDLE ou pelo console:

C:\Users\nilo\Desktop>\Python27\python.exe test.py
Acentos: ├í├®├¡├│├║├º├ú├Á
Acentos2: áéíóúçãõ


Você deve ter obtido bons resultados apenas na linha do Acentos2. Se a string não é marcada com unicode, vai ser simplesmente impressa como uma sequência de bytes, sem tradução. Se tiver o u na frente, como em acentos2, o Python saca que precisa traduzir de unicode para cp850, no caso do console aqui de casa. Já no Linux, as duas linhas produzem resultados corretos!

O encoding: utf-8 informa apenas a codificação do código fonte. Ou seja, é apenas uma dica de como os caracteres deveriam estar codificados. Para que funcione corretamente, seu editor de texto tem que estar configurado para UTF-8 também. Se misturar, é desastre na certa. Eu recomendo o PSPad no Windows para editar com UTF-8. Para verificar o encoding de um arquivo que você não conhece, ou para ter certeza de qual codificação seu editor realmente utilizou, use um visualizador binário como o HxD [3]. No hex edit do PS Pad, atenção que ele mostra os caracteres em Unicode, mesmo se a codificação for UTF-8. Isso para lembrar que UTF-8 é uma representação ou forma de codificação de caracteres Unicode. O Notepad++ pode também ser usado para editar e codificar arquivos em UTF-8.
No Mac e no Linux, tente o hexdump -C arquivo
Quando o arquivo esta codificado corretamente em utf-8, você deve ter mais de um byte para os caracteres acentuados.

Por exemplo, o programa acima, criado no vim do Ubuntu:
nilo@linuxvm:~$ hexdump -C test.py
00000000  23 20 2d 2a 2d 20 63 6f  64 69 6e 67 3a 20 20 75  |# -*- coding:  u|
00000010  74 66 2d 38 20 2d 2a 2d  0a 70 72 69 6e 74 20 22  |tf-8 -*-.print "|
00000020  41 63 65 6e 74 6f 73 3a  20 c3 a1 c3 a9 c3 ad c3  |Acentos: .......|
00000030  b3 c3 ba c3 a3 c3 b5 c3  a7 22 0a 70 72 69 6e 74  |.........".print|
00000040  20 22 41 63 65 6e 74 6f  73 32 3a 20 c3 a1 c3 a9  | "Acentos2: ....|
00000050  c3 ad c3 b3 c3 ba c3 a3  c3 b5 c3 a7 22 0a 0a     |............"..|
0000005f


Um site bacana é esse aqui: http://www.utf8-chartable.de/

Uma vez resolvido o problema de codificação dos fontes, restam ainda:
* A codificação do console
* A codificação dos arquivos de dados
* Codificação do banco de dados

Tanto o Mac quanto Linux usam UTF-8 por padrão. O Windows usa a cp 1252 (GUI), compatível com iso8859_1. Cuidado também se você troca arquivos entre máquinas Windows, Linux e Mac. E nunca misture duas codificações no mesmo arquivo, pois isto gera erros difíceis de detectar e resolver.
É fácil misturar quando se faz append em um arquivo, vindo de outra máquina ou mesmo gerado em um outro programa.
O Windows em chinês, russo e outras línguas não utilizam a cp1252! Por isso UTF-8 é uma boa pedida, pois consegue codificar caracteres Unicode com um ou vários bytes, dependendo da necessidade.

O Python 3 resolve muito destes problemas, mas a documentação diz[1]:


Files opened as text files (still the default mode for open()) always use an encoding to map between strings (in memory) and bytes (on disk). Binary files (opened with a b in the mode argument) always use bytes in memory. This means that if a file is opened using an incorrect mode or encoding, I/O will likely fail loudly, instead of silently producing incorrect data. It also means that even Unix users will have to specify the correct mode (text or binary) when opening a file. There is a platform-dependent default encoding, which on Unixy platforms can be set with the LANG environment variable (and sometimes also with some other platform-specific locale-related environment variables). In many cases, but not all, the system default is UTF-8; you should never count on this default. Any application reading or writing more than pure ASCII text should probably have a way to override the encoding. There is no longer any need for using the encoding-aware streams in the codecs module.

A parte que sublinhei diz: "... o padrão do sistema é UTF-8; você não deve contar nunca com este padrão..."
Resumindo, é um assunto que merece ser estudado, pois causa problemas  "mágicos" que sempre aparecem.

Um texto que explica tudo com detalhes pode ser encontrado em [2].

[]

Nilo Menezes
[1] http://docs.python.org/release/3.0.1/whatsnew/3.0.html
[2] http://wiki.python.org.br/TudoSobrePythoneUnicode
[3] http://mh-nexus.de/en/hxd/

Um comentário:

Brendon Lincon Cunha Correia disse...

Por muito tempo estava só colocando as linhas:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

pensando que o python acentuaria.

Cara, muito obrigado mesmo.