4. Como lidar com strings
Esta seção explica como as cadeias de caracteres são representadas no Python 2.x, no Python 3.x e no GTK+ e discute erros comuns que surgem ao trabalhar com strings.
4.1. Definições
Conceitualmente, uma string é uma lista de caracteres como “A”, “B”, “C” ou “É”. Caracteres são representações abstratas e seu significado depende do idioma e do contexto em que são usados. O padrão Unicode descreve como os caracteres são representados por pontos de código. Por exemplo, os caracteres acima são representados com os pontos de código U+0041, U+0042, U+0043 e U+00C9, respectivamente. Basicamente, os pontos de código são números no intervalo de 0 a 0x10FFFF.
Como mencionado anteriormente, a representação de uma string como uma lista de pontos de código é abstrata. Para converter essa representação abstrata em uma sequência de bytes, a string Unicode deve ser codificada. A forma mais simples de codificação é ASCII e é executada da seguinte maneira:
Se o ponto de código for < 128, cada byte é o mesmo que o valor do ponto de código.
Se o ponto de código for 128 ou maior, a string Unicode não poderá ser representada nessa codificação. (Python dispara uma exceção
UnicodeEncodeError
neste caso.)
Embora a codificação ASCII seja simples de aplicar, ela só pode codificar 128 caracteres diferentes, o que não é suficiente. Uma das codificações mais usadas para resolver esse problema é o UTF-8 (ele pode manipular qualquer ponto de código Unicode). UTF significa “Formato de Transformação Unicode”, do inglês “Unicode Transformation Format”, e “8” significa que números de 8 bits são usados na codificação.
4.2. Python 2
4.2.1. Suporte a Unicode do Python 2.x
O Python 2 vem com dois tipos diferentes de objetos que podem ser usados para representar strings str
e unicode
. Instâncias do último são usadas para expressar strings Unicode, enquanto instâncias do tipo str
são representações de byte (a string codificada). Sob o capô, Python representa strings Unicode como números inteiros de 16 ou 32 bits, dependendo de como o interpretador Python foi compilado. Strings Unicode podem ser convertidas em strings de 8 bits com unicode.encode()
:
>>> unicode_string = u"Fu\u00dfb\u00e4lle"
>>> print unicode_string
Fußbälle
>>> type(unicode_string)
<type 'unicode'>
>>> unicode_string.encode("utf-8")
'Fu\xc3\x9fb\xc3\xa4lle'
As strings de 8 bits do Python têm um método str.decode()
que interpreta a string usando a codificação fornecida:
>>> utf8_string = unicode_string.encode("utf-8")
>>> type(utf8_string)
<type 'str'>
>>> u2 = utf8_string.decode("utf-8")
>>> unicode_string == u2
True
Infelizmente, o Python 2.x permite que você misture unicode
e str
se a string de 8 bits contivesse apenas bytes de 7 bits (ASCII), mas obteria UnicodeDecodeError
se contivesse valores não-ASCII:
>>> utf8_string = " sind rund"
>>> unicode_string + utf8_string
u'Fu\xdfb\xe4lle sind rund'
>>> utf8_string = " k\xc3\xb6nnten rund sein"
>>> print utf8_string
könnten rund sein
>>> unicode_string + utf8_string
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 2:
ordinal not in range(128)
4.2.2. Unicode no GTK+
O GTK+ usa strings codificadas em UTF-8 para todo o texto. Isto significa que se você chamar um método que retorna uma string, você sempre obterá uma instância do tipo str
. O mesmo se aplica aos métodos que esperam um ou mais strings como parâmetro, eles devem ser codificados em UTF-8. No entanto, por conveniência, o PyGObject converterá automaticamente qualquer instância unicode
para str
se fornecido como argumento:
>>> from gi.repository import Gtk
>>> label = Gtk.Label()
>>> unicode_string = u"Fu\u00dfb\u00e4lle"
>>> label.set_text(unicode_string)
>>> txt = label.get_text()
>>> type(txt), txt
(<type 'str'>, 'Fu\xc3\x9fb\xc3\xa4lle')
>>> txt == unicode_string
__main__:1: UnicodeWarning: Unicode equal comparison failed to convert
both arguments to Unicode - interpreting them as being unequal
False
Observe o aviso no final. Apesar de chamarmos Gtk.Label.set_text()
com uma instância de unicode
como argumento, Gtk.Label.get_text()
sempre retornará uma instância str
. Assim, txt
e unicode_string
não são iguais.
Isto é especialmente importante se você quiser internacionalizar seu programa usando gettext. Você precisa ter certeza de que gettext retornará strings de 8 bits codificadas em UTF-8 para todos os idiomas. Em geral, recomenda-se não usar objetos unicode
em aplicativos GTK+ e usar somente objetos codificados em UTF-8 str
, já que o GTK+ não se integra totalmente a objetos unicode
. Caso contrário, você teria que decodificar os valores de retorno para cadeias de caracteres Unicode cada vez que você chamar um método GTK+:
>>> txt = label.get_text().decode("utf-8")
>>> txt == unicode_string
True
4.3. Python 3
4.3.1. Suporte a Unicode do Python 3.x
Desde o Python 3.0, todas as strings são armazenadas como Unicode em uma instância do tipo str
. Strings codificadas, por outro lado, são representadas como dados binários na forma de instâncias do tipo bytes
. Conceitualmente, str
refere-se a texto, enquanto bytes
refere-se a dados. Use str.encode()
para ir de str
para bytes
e bytes.decode()
para ir de bytes
para str
.
Além disso, não é mais possível misturar strings Unicode com strings codificadas, porque resultará em um TypeError
:
>>> text = "Fu\u00dfb\u00e4lle"
>>> data = b" sind rund"
>>> text + data
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't convert 'bytes' object to str implicitly
>>> text + data.decode("utf-8")
'Fußbälle sind rund'
>>> text.encode("utf-8") + data
b'Fu\xc3\x9fb\xc3\xa4lle sind rund'
4.3.2. Unicode no GTK+
Como consequência, as coisas são muito mais limpas e consistentes com o Python 3.x porque o PyGObject irá automaticamente codificar/decodificar para/de UTF-8 se você passar uma string para um método ou um método retornar uma string. Strings, ou text, sempre serão representados como instâncias de str
apenas:
>>> from gi.repository import Gtk
>>> label = Gtk.Label()
>>> text = "Fu\u00dfb\u00e4lle"
>>> label.set_text(text)
>>> txt = label.get_text()
>>> type(txt), txt
(<class 'str'>, 'Fußbälle')
>>> txt == text
True
4.4. Referências
O que há de novo no Python 3.0 descreve os novos conceitos que distinguir claramente entre texto e dados.
O Unicode HOWTO aborda o suporte do Python 2.x a Unicode e explica vários problemas que as pessoas comumente encontram ao tentar trabalhar com o Unicode.
O Unicode HOWTO for Python 3.x discute o suporte a Unicode no Python 3.x.
A tabela de codificação UTF-8 e os caracteres Unicode contém uma lista de pontos de código Unicode e sua respectiva codificação UTF-8.