associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Revisão 7e 2008-02-21 20:29:07

Excluir mensagem

TudoSobrePythoneUnicode

Tudo Sobre Python e Unicode

Tradução de [http://boodebr.org/main/python/all-about-python-and-unicode "All about Python and Unicode"] por Nilo Menezes.

TableOfContents

Um ponto de partida

Duas semanas antes de começar a escrever este documento, meu conhecimento sobre [http://www.python.org/ Python] e [http://www.unicode.org/ Unicode] era algo como:

  • Tudo que precisa para usar Unicode em Python é passar suas strings para unicode()

Agora, onde eu fui arranjar uma idéia tão estranha? Ah, certo, do [http://docs.python.org/tut/node5.html#SECTION005130000000000000000 tutorial de Python sobre Unicode], que afirma:

  • "Criar strings Unicode em Python é tão simples quanto criar strings normais":

    >>> u'Alô Mundo !' u'Alô Mundo !'

Ainda que este exemplo seja tecnicamente correto, ele pode enganar o iniciante em Unicode, uma vez que ele esconde diversos detalhes necessários para o uso prático. Esta explicação ultra simplificada me deu um entendimento completamente errado sobre como Unicode funciona em Python.

Se você também foi guiado pelo caminho ultra simplificado, então este tutorial irá provavelmente ajudá-lo. Este tutorial contém um conjunto de exemplos, testes e demonstrações que documentam meu "reaprendizado" da forma correta de trabalhar com Unicode em Python. Ele inclui problemas de portabilidade, assim como questões que surgem quando lidamos com [http://www.w3.org/MarkUp/HTML HTML], [http://www.w3.org/XML/ XML] e sistemas de arquivo.

Aproveitando, Unicode é justamente simples, eu só queria ter aprendido a usá-lo corretamente da primeira vez.

Onde começar?

Em alto nível, computadores utilizam três tipos de representação de textos:

  1. ASCII
  2. Conjuntos de caracteres Multibyte
  3. Unicode

Eu acho que Unicode é mais fácil de entender se você entender como ele evoluiu a partir do código ASCII. A parte seguinte é umá breve sinópse desta evolução.

Do ASCII ao Multibyte

No início, existia ASCII. (OK, também existia o [http://www.dynamoo.com/technical/ascii-ebcdic.htm#asciibetter EBCDIC]), mas este nunca pegou fora dos mainframes, então eu o estou omitindo aqui). O conjunto de caracteres ASCII contém 256 caracteres, como você pode ver nesta [http://www.asciitable.com/ tabela ASCII]. Ainda que 256 caracteres sejam disponíveis, os primeiros 128 (códigos de 0 a 127) são normalmente os mais utilizados. Na verdade, os primeiros sistemas de email só permitiam a transmissão de caracteres 0-127 (isto é texto de "7-bits") e de fato isto continua valendo para muitos sistemas ainda hoje. Como você pode constatar na tabela, o código ASCII é suficiente apenas para documentos escritos em inglês.

Problemas começaram a surgir quando computadores começaram a ser usados em países onde não bastavam apenas os caracteres ASCII. O código ASCII não possui a capacidade de ser utilizado em textos escritos em grego, cirílico ou japonês, para citar poucos. Além disso, textos em japonês precisam de milhares de caracteres, logo não há como fazer isso usando apenas 8-bits. Para superar esta limitação, Conjuntos de Caracteres Multibyte foram inventados. A maioria (senão todos) dos Conjuntos de Caracteres Multibyte se aproveitam do fato de que apenas os 128 primeiros caracteres do código ASCII são comumente utilizados (códigos 0-127 em decimal, ou 0x00-0x7f em hexadecimal). Os códigos superiores (128..255 em decimal, ou 0x80-0xff em hexadecimal) são utilizados para definir conjuntos estendidos (não utilizados em inglês).

Vamos olhar um exemplo: Shift-JIS é uma das codificações para texto em japonês. Você pode ver a [http://www.rikai.com/library/kanjitables/kanji_codes.sjis.shtml tabela de caracteres aqui]. Observe que o primeiro byte de cada caractere começa com um valor hexadecimal entre 0x80 e 0xfc. Esta é uma propriedade interessante, porque ela faz com que texto em japonês e inglês possam ser misturados livremente! A string "Hello World!" é perfeitamente válida na codificação Shift-JIS para textos em inglês. Quando analisamos, caracter a caracter (parse), um texto que utiliza o Shift-JIS, se você encontrar um byte na faixa 0x80-0xff, você saberá que este é o primeiro caracter de uma seqüência de dois códigos. Caso contrário, é um caracter de apenas um byte como no ASCII comum.

Isto funciona muito bem enquanto você trabalhar apenas em japonês, mas o que acontece se você trocar para o [http://czyborra.com/charsets/iso8859.html#ISO-8859-7 conjunto de caracteres gregos]? Como você pode observar, na tabela do ISO-8859-7 os códigos de 0x80-0xff são definidos de uma forma completamente diferente do Shift-JIS. Logo, ainda que você possa misturar inglês com japonês, você não pode misturar grego e japonês uma vez que esses códigos se sobrepõem. Este é um problema comum ao se misturar conjuntos de caracteres multibyte.

De Multibyte a Unicode

Para resolver o problema de misturar linguagens diferentes, o código Unicode propõe combinar todos os conjuntos de caracteres do mundo em uma única tabela gigantesca. Dê uma olhada no [http://www.unicode.org/charts/ conjunto de caracteres Unicode].

De início, parecem existir tabelas diferentes para cada linguagem, assim você pode não perceber melhorias em relação ao ASCII. Na realidade, todos estão na mesma tabela, e estão agrupados aqui simplesmente para facilitar a referência(por humanos). A princial característica a observar é que uma vez que todos estes caracteres são parte da mesma tabela, não há sobreposição de código entre eles como ocorre no mundo ASCII/Multibyte. Isto permite a documentos Unicode misturar linguagens livremente sem conflitos de codificação.

Terminologia Unicode

Vamos olhar a [http://www.unicode.org/charts/PDF/U0370.pdf tabela de caracteres Gregos] a pegar alguns caracteres:

Exemplos de Símbolos Unicode

03A0

Π

Greek Capital Letter Pi (Grego Letra Maiúscula Pi)

03A3

Σ

Greek Capital Letter Sigma (Grego Letra Maiúscula Sigma)

03A9

Ω

Greek Capital Letter Omega (Grego Letra Maiúscula Omega)

É comum referenciar estes símbolos usando a notação U+NNNN, por exemplo U+03A0. Logo, nós poderíamos definir uma string que contenha estes caracteres usando a seguinte notação (eu adicionei colchetes para facilitar o entendimento):

uni = {U+03A0} + {U+03A3} + {U+03A9} 

Agora, mesmo que saibamos exatamente o que 'uni' representa (ΠΣΩ), observe que não como:

  • Imprimir uni na tela.
  • Salvar uni em um arquivo.
  • Dizer quantos bytes uni ocupa

Por que? Porque uni é uma string Unicode idealizada - nada mais que um conceito até agora. Veremos brevemente como imprimir, salvar e manipular, mas por enquanto, lembre-se desta última afirmação: Não há como dizer quantos bytes serão necessários para armazenar uni. De fato, você deve esquecer tudo sobre bytes e pensar em strings Unicode como conjuntos de símbolos.

Nome da Codificação

Representação Binária

ISO-8859-7

\xD9 (codificação grega nativa)

UTF-8

\xCE\xA9

UTF-16

\xFF\xFE\xA9\x03

UTF-32

\xFF\xFE\x00\x00\xA9\x03\x00\x00

Cada um destas representações é uma codificação válida de Ω, mas tentar trabalhar com bytes como acima não é melhor que lidar com o mundo ASCII/Multibyte. É por isso que eu digo que você deve pensar em Unicode como símbolos (Ω) e não como bytes.

Texto Unicode em Python

Para converter nossa string Unicode idealizada uni (ΠΣΩ) em de forma a poder ser utilizada, nós temos que observar algumas coisas:

  1. Representação de literais Unicode
  2. Convertendo Unicode para binário
  3. Convertendo binário para Unicode
  4. Usando operações com strings

Convertendo símbolos Unicode em literais Python

Criar uma string Unicode com símbolos é muito fácil. Vamos relembrar os símbolos gregos:

Exemplos de Símbolos Unicode

03A0

Π

Greek Capital Letter Pi (Grego Letra Maiúscula Pi)

03A3

Σ

Greek Capital Letter Sigma (Grego Letra Maiúscula Sigma)

03A9

Ω

Greek Capital Letter Omega (Grego Letra Maiúscula Omega)

Vamos dizer que nós queremos uma string Unicode com esses caracteres, mais alguns caracteres ASCII a moda antiga.

Pseudo-código:

uni = 'abc_' + {U+03A0} + {U+03A3} + {U+03A9} + '.txt'

Fazendo isto em Python:

uni = u"abc_\u03a0\u03a3\u03a9.txt"

Algumas coisas a observar:

  • Caracteres ASCII comuns podem ser escritos normalmente. Você pode simplesmente colocar "a", não precisa escrever o símbomo Unicode "\u0061". (Mas lembre-se, "a" é realmente {U+0061}; não existe essa coisa de símbolo Unicode "a".)

  • A seqüência de escape \u é usada para representar códigos Unicode.

    • Isto é algo como o tradicional estilo-C \xNN para inserir valores binários. Entretanto, uma olhada na tabela Unicode nos revela valores de até 6 dígitos. Estes não podem ser convenientemente representados por \xNN, assim o \u foi inventado.

    • Para valores Unicode até (e incluindo) 4 dígitos, use a versão de 4 dígitos: \uNNNN (Observe que você deve usar todos os 4 dígitos, usando zeros a esquerda se necessário).

    • Para valore Unicode maiores que 4 dígitos, use a versão de 8 dígitos: \UNNNNNNNN (Observe que você deve usar todos os 8 dígitos, usando zeros a esquerda se necessário)

Aqui está um outro exemplo:

Pseudo-código:

uni = {U+1A} + {U+B3C} + {U+1451} + {U+1D10C}

Python:

   1 uni = u'\u001a\u0bc3\u1451\U0001d10c'

Note como eu adicionei zeros a cada um destes valores para que eles tivessem de 4 a 8 dígitos. Você receberá um error do Python se não fizer isso. Observe também que você pode usar tanto letras maiúsculas quanto minúsculas no código. O exemplo abaixo resultaria exatamente na mesma coisa:

Python:

   1 uni = u'\u001A\u0BC3\u1451\U0001D10C'

Por que o "print" não funciona?

Você lembra que anteriormente eu disse que uni não possuía uma representação determinada no computador. Então, o que acontece se nós tentarmos imprimir uni ?

   1 uni = u"\u001A\u0BC3\u1451\U0001D10C"
   2 print uni

Você veria:

Traceback (most recent call last):
  File "t6.py", line 2, in ?
    print uni
UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-4:
ordinal not in range(128)

O que aconteceu? Bem, você pediu ao Python para imprimir uni, mas uma vez que uni não tem uma representação determinada no computador, Python tem que primeiro converter uni em alguma forma imprimível. Já que você não disse ao Python como fazer a conversão, ele assumiu que você queria ASCII. Infelizmente, ASCII só manipula valores entre 0 e 127, e uni contém valores fora da faixa, por isso você tem um erro.

Um método rápido de imprimir uni é usar o método do Python chamado repr():

uni = u"\u001A\u0BC3\u1451\U0001D10C"
print repr(uni)

Que imprime:

u'\x1a\u0bc3\u1451\U0001d10c'

Que, claro, faz sentido, uma fez que é exatamente como nós definimos uni. Mas repr(uni) é simplesmente inútil no mundo real assim como uni. O que nós realmente precisamos é aprender sobre codecs.

Codecs

Codecs Em geral, os codecs da linguagem Python permitem transformações arbitrárias entre objetos. Entretanto, no contexto deste artigo, é suficiente entender codecs como funções que transformam objetos Unicode em strings Python em formato binário, e vice-versa.

Por que precisamos deles? Objetos Unicode não possuem uma representação determinada no computador. Antes que um objeto Unicode possa ser impresso, armazenado em disco, ou enviado pela rede, ele deve ser codificado em um representação especifica. Isto é feito utilizando-se um codec. Alguns codecs populares que você pode ter ouvido falado no seu dia-a-dia: ascii, iso-8859-7, UTF-8, UTF-16.

De Unicode para binário

Para converter um valor Unicode em uma representação binária, você chama o método .encode com o nome do codec. Por exemplo, para converter um valor Unicode par UTF-8:

binary = uni.encode("utf-8")

O que você acha tornarmos uni mais interessante, adicionando alguns caracteres comuns:

uni = u"Hello\u001A\u0BC3\u1451\U0001D10CUnicode" 

Agora, vamos observar como diferente codecs representam uni. Aqui um pequeno programa de teste:

test_codec01.py

   1 if __name__ == '__main__':
   2 
   3     # Define nossa string Unicode
   4     uni = u"Hello\u001A\u0BC3\u1451\U0001D10CUnicode"
   5 
   6     # UTF-8 e UTF-16 podem codificar completamente qualquer string Unicode
   7     print "UTF-8", repr(uni.encode('utf-8'))
   8     print "UTF-16", repr(uni.encode('utf-16'))
   9 
  10     # ASCII pode apenas codificar valore entre 0-127. Abaixo, dizemos ao Python
  11     # para trocar caracteres que não podem ser codificados por '?'
  12     print "ASCII",uni.encode('ascii','replace')
  13 
  14     # ISO-8859-1 é similar ao ASCII
  15     print "ISO-8859-1",uni.encode('iso-8859-1','replace')

Que produz a seguinte saída:

UTF-8 'Hello\x1a\xe0\xaf\x83\xe1\x91\x91\xf0\x9d\x84\x8cUnicode'
UTF-16 '\xff\xfeH\x00e\x00l\x00l\x00o\x00\x1a\x00\xc3\x0bQ\x144
        \xd8\x0c\xddU\x00n\x00i\x00c\x00o\x00d\x00e\x00'
ASCII Hello????Unicode
ISO-8859-1 Hello????Unicode

Note que eu continuei a usar repr() para imprimir as strings UTF-8 e UTF-16. Por que? Bem, de outra forma os valores teriam sido impressos na tela utilizando seu conteúdo binário, o que seria difícil de mostrar neste documento.

De binário para Unicode

Digamos que alguém tenha dado a você um objeto Unicode codificado com UTF-8. Como converter de volta para Unicode? Você pode ingenuamente tentar isso:

A forma ingênua (e errada)

|| uni = unicode( utf8_string )

Por que errada? Aqui temos um programa que faz exatamente isso:

   1 uni = u"Hello\u001A\u0BC3\u1451\U0001D10CUnicode"
   2 utf8_string = uni.encode('utf-8')
   3 
   4 # Ingenuamente converte de volta para Unicode
   5 uni = unicode(utf8_string)

Aqui o que acontece:

Traceback (most recent call last):
    File "t6.py", line 5, in ?
    uni = unicode(utf8_string)

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

Você sabe, a função unicode() possui realmente dois parâmetros:

   1 def unicode(string, encoding):
   2      ....

No exemplo acima, nós omitimos a codificação (encoding) logo o Python, na melhor das intenções, assumiu mais uma vez que nós queríamos ASCII (nota 1), e nos deu a coisa errada.

Aqui está a forma correta de fazer isso:

   1 uni = u"Hello\u001A\u0BC3\u1451\U0001D10CUnicode"
   2 utf8_string = uni.encode('utf-8')
   3 
   4 # Tem que decodificar usando o mesmo codificador usado na codificação!
   5 uni = unicode(utf8_string,'utf-8')
   6 print "De volta para UTF-8: ",repr(uni)

Que resulta em:

De volta para UTF-8:  u'Hello\x1a\u0bc3\u1451\U0001d10cUnicode'

Operações com Strings

Os exemplos acima devem ter dado uma boa idéia do por quê você quer evitar tratar valores Unicode em formato binário o máximo possível! A versão UTF-8 tinha 23 bytes, a versão UTF-16 tinha 36 bytes, a versão ASCII tinha 16 bytes (mas ela descartou completamente 4 valores Unicode) e de forma similar com ISO-8859-1.

Isto é o por quê, desde o começo, sugeri que você esquecesse sobre bytes!

A boa nova é que uma vez que você tem um objeto Unicode, ele se comporta exatemente como um objeto string comum, logo não há sintaxe adicional a aprender (outra que os códigos de escape \u e \U). Aqui temos um pequeno exemplo que mostra objetos Unicode se comportando do jeito que você espera:

test_strings01.py

   1 if __name__ == '__main__':
   2 
   3     uni = u"Hello\u001A\u0BC3\u1451\U0001D10CUnicode"
   4 
   5     print "uni = ",repr(uni)
   6 
   7     print "len(uni) = ",len(uni)
   8 
   9     # print the "Hello" part
  10     print "uni[:5] = ",uni[:5]
  11 
  12     # print the Unicode characters one at a time
  13     print "uni[5] = ",repr(uni[5])
  14     print "uni[6] = ",repr(uni[6])
  15     print "uni[7] = ",repr(uni[7])
  16 
  17     # Depending on how Python was compiled, \U characters
  18     # may be stored as two Unicode characters -- see the
  19     # section "A wrinkle in \U" below for more details ...
  20     print "uni[8] = ",repr(uni[8])
  21     print "uni[9] = ",repr(uni[9])
  22 
  23     # print the "Unicode" text at the end
  24     print "uni[10:] = ",repr(uni[10:])

Executando este exemplo, obtemos o seguinte resultado:

uni =  u'Hello\x1a\u0bc3\u1451\U0001d10cUnicode'
len(uni) =  17
uni[:5] =  Hello
uni[5] =  u'\x1a'
uni[6] =  u'\u0bc3'
uni[7] =  u'\u1451'
uni[8] =  u'\ud834'
uni[9] =  u'\udd0c'
uni[10:] =  u'Unicode'

O pau do \U

Dependendo de como seu interpretador Python foi compilado, ele armazena objetos Unicode internamente em UTF-16 (2 bytes por caracter) ou UTF-32 (4 bytes por caracter). Infelizmente este nível de detalhe é exposto na interface normal de string.

Para caracteres de 4 dígitos(16 bits) como \u03a0, não há diferença.

   1 a = u'\u03a0'
   2 print len(a)

Mostrará tamanho 1, não importa como o seu Python foi compilado, e a[0] será sempre \u03a0. Entretanto, para caracteres de 8 dígitos (32 bits), como \U0001FF00, você verá uma diferença. Obviamente, valores de 32 bits não podem ser diretamente representados em código de 16 bits, logo um par de valores de 16 bits é usado. (Códigos 0xD800 - 0xDFFF), chamdos "surrogate pairs", são reservados para estas sequências de dois caracteres. Estes valores são invalidos se utilizados separadamente pela especificação Unicode.)

A programa exemplo que mostra o que acontece:

O que acontece com \U...

   1 a = u'\U0001ff00'
   2 print "Length:",len(a)
   3 
   4 print "Chars:"
   5 for c in a:
   6     print repr(c)

Se você executar este exemplo em um Python UTF-16, você verá:

Resultado, Python UTF-16

Length: 2
Chars:
u'\ud83f'
u'\udf00'

Em um Python UTF-32, você verá:

Resultado com Python UTF-32:

Length: 1
Chars:
u'\U0001ff00'

Este é um detalhe irritante de se preocupar. Eu escrevi um módulo que deixa você passar caracter a caracter dentro de uma string Unicode, independete de você utilizar Python UTF-16 ou Python UTF-32. Ele é chamado xmlmap e é parte dos [http://freshmeat.net/projects/gnosisxml/ utilitários Gnosis]. Aqui estão dois exemplos, um usando o xmlmap e outro não.

Sem xmlmap

   1 a = u'A\U0001ff00C\U0001fafbD'
   2 print "Length:",len(a)
   3 
   4 print "Chars:"
   5 for c in a:
   6     print repr(c)

Resultados sem o xmlmap, em um Python UTF-16

Length: 7
Chars:
u'A'
u'\ud83f'
u'\udf00'
u'C'
u'\ud83e'
u'\udefb'
u'D'

Agora, usando a função usplit() para conseguir os caracteres um por um, combinando valores quando necessário:

Com xmlmap

   1 from gnosis.xml.xmlmap import usplit
   2 
   3 a = u'A\U0001ff00C\U0001fafbD'
   4 print "Length:",len(a)
   5 
   6 print "Chars:"
   7 for c in usplit(a):
   8     print repr(c)

Resultados com xmlmap, em um Python UTF-16

Length: 7
Chars:
u'A'
u'\U0001ff00'
u'C'
u'\U0001fafb'
u'D'

Agora você terá resultados idênticos, independente de como seu interpretador Python foi compilado. (Note que o tamanho continua o mesmo, mas usplit() combinou os "surrogate pairs" de forma que você não os vê aqui.)

Bugs do Python 2.0 & 2.1

Você pode pensar quem liga quando isso vem do Python 2.0 e 2.1, mas ao escrevermos código supostamente portátil, isso importa!

O Python 2.0.x e 2.1.x tem um erro fatal quando tenta manipular códigos de um caracter na faixa \uD800-\uDFFF.

O exemplo abaixo apresenta o problema:

   1  u = unichr(0xd800)
   2  print "Orig: ",repr(u)
   3 
   4  # Cria utf-8 a partir de '\ud800'
   5  ue = u.encode('utf-8')
   6  print "UTF-8: ",repr(ue)
   7 
   8  # Decodifica de volta para Unicode
   9  uu = unicode(ue,'utf-8')
  10  print "Back: ",repr(uu)

Rodando isso no Python 2.2 e versões superiores produz o resultado esperado:

 Orig:  u'\ud800'
 UTF-8:  '\xed\xa0\x80'
 Back:  u'\ud800'

Python 2.0.x retorna:

 Orig:  u'\uD800'
 UTF-8:  '\240\200'
 Traceback (most recent call last):
   File "test_utf8_bug.py", line 9, in ?
     uu = unicode(ue,'utf-8')
 UnicodeError: UTF-8 decoding error: unexpected code byte

Python 2.1.x retorna:

 Orig:  u'\ud800'
 UTF-8:  '\xa0\x80'
 Traceback (most recent call last):
   File "test_utf8_bug.py", line 9, in ?
     uu = unicode(ue,'utf-8')
 UnicodeError: UTF-8 decoding error: unexpected code byte

Como voocê pode ver, ambos falharam ao codificar \ud800 quando usado como um caracter único. Ainda que seja verdade que caracteres entre 0xD800 .. 0xDFF não sejam válidos quando usados sozinhos, o fato é que Python lhe deixa usá-los.

Mas se são inválidos, por que o Python se importa?

Eu arranjei um bom exemplo, completamente por acidente enquanto trabalhava no código deste tutorial. Crie dois arquivos em Python:

aaa.py

   1 x = u'\ud800'

bbb.py

   1 import sys
   2 sys.path.insert(0,'.')
   3 import aaa

Agora, use o Python 2.0.x/2.1.x para executar bbb.py duas vezes (precisa executar duas vezes para que ele carregue aaa.pyc na segunda vez). Na segunda execução, você terá:

Traceback (most recent call last):
    File "bbb.py", line 3, in ?
      import aaa
  UnicodeError: UTF-8 decoding error: unexpected code byte

É isso mesmo: o Python 2.0.x/2.1.x não é capaz de recarregar seu próprio bytecode de um arquivo .pyc se o fonte contém uma string como \u0d800. Uma forma de resolver este problema seria usar unichr(0xd800) no lugar de \ud800 (e é isto que o gnosis.xml.pickle faz).

Python como um "recodificador universal"

Até este ponto, eu converti Unicode de e para UTF para fins de demonstração. Entretanto, Python lhe permite fazer muito mais que isso. Python permite que você converta praticamente qualquer string multibyte em Unicode (e vice-versa). Implementando todas estas conversões dá um monte de trabalho. Felizmente, já foi feito, tudo que temos que fazer é usar.

Vamos revisitar nossa tabela grega, mas desta vez eu vou apresentar os caracteres tanto em Unicode quanto em ISO-8859-7 ("grego nativo").

Caracter

Nome

Unicode

ISO-8859-7

Π

Greek Capital Letter Pi (Grego Letra Maiúscula Pi)

03A0

0xD0

Σ

Greek Capital Letter Sigma (Grego Letra Maiúscula Sigma)

03A3

0xD3

Ω

Greek Capital Letter Omega (Grego Letra Maiúscula Omega)

03A9

0xD9

Com Python, usando unicode() e .encode() é trivial converter entre eles.

   1 # {Pi}{Sigma}{Omega} como uma string codificada com ISO-8859-7
   2 b = '\xd0\xd3\xd9'
   3 
   4 # Converte para Unicode ('formato universal')
   5 u = unicode(b, 'iso-8859-7')
   6 print repr(u)
   7 
   8 # ... e de volta para ISO-8859-7
   9 c = u.encode('iso-8859-7')
  10 print repr(c)

Mostra:

u'\u03a0\u03a3\u03a9'
\xd0\xd3\xd9

Você também pode usar Python como um "recodificador universal". Digamos que você tenha recebido um arquivo em japonês, usando a codificação [http://www.rikai.com/library/kanjitables/kanji_codes.sjis.shtml ShiftJIS] e queira convertê-lo para a codificação [http://www.rikai.com/library/kanjitables/kanji_codes.euc.shtml EUC-JP]:

   1 txt = ... texto codificado com ShiftJIS ...
   2 
   3 # converte para Unicode ("formato universal")
   4 u = unicode(txt, 'shiftjis')
   5 
   6 # converte para EUC-JP
   7 out = u.encode('eucjp')

Claro que isto só funciona quando convertemos entre conjuntos de caracteres compatíveis. Tentar converter entre conjuntos de caracteres japoneses e gregos desta forma não funcionaria.

Agora começa a diversão... Unicode e o Mundo Real

Nomes de arquivo com caracteres Unicode

Microsoft Windows

Unix/POSIX/Linux

Mac OS/X

Unicode e HTML

Unicode e XML

Unicode e diretórios compartilhados (Samba)

Sumário