O que devo fazer no Python para descobrir qual codificação uma string possui?
{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
O que devo fazer no Python para descobrir qual codificação uma string possui?
{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
Respostas:
No Python 3, todas as strings são seqüências de caracteres Unicode. Há um bytes
tipo que contém bytes brutos.
No Python 2, uma string pode ser do tipo str
ou do tipo unicode
. Você pode dizer qual código usando algo parecido com isto:
def whatisthis(s):
if isinstance(s, str):
print "ordinary string"
elif isinstance(s, unicode):
print "unicode string"
else:
print "not a string"
Isso não distingue "Unicode ou ASCII"; apenas distingue os tipos Python. Uma cadeia de caracteres Unicode pode consistir em caracteres puramente no intervalo ASCII, e uma cadeia de bytes pode conter dados ASCII, Unicode codificados ou mesmo não textuais.
Você pode usar type
ou isinstance
.
No Python 2:
>>> type(u'abc') # Python 2 unicode string literal
<type 'unicode'>
>>> type('abc') # Python 2 byte string literal
<type 'str'>
No Python 2, str
é apenas uma sequência de bytes. Python não sabe qual é a sua codificação. O unicode
tipo é a maneira mais segura de armazenar texto. Se você quiser entender mais isso, recomendo http://farmdev.com/talks/unicode/ .
No Python 3:
>>> type('abc') # Python 3 unicode string literal
<class 'str'>
>>> type(b'abc') # Python 3 byte string literal
<class 'bytes'>
No Python 3, str
é como o Python 2 unicode
e é usado para armazenar texto. O que foi chamado str
no Python 2 é chamado bytes
no Python 3.
Você pode ligar decode
. Se gerar uma exceção UnicodeDecodeError, não será válido.
>>> u_umlaut = b'\xc3\x9c' # UTF-8 representation of the letter 'Ü'
>>> u_umlaut.decode('utf-8')
u'\xdc'
>>> u_umlaut.decode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
unicode(s, "ascii")
ou algo
str(s, "ascii")
No python 3.x, todas as strings são seqüências de caracteres Unicode. e fazer a verificação isinstance para str (que significa string unicode por padrão) deve ser suficiente.
isinstance(x, str)
Com relação ao python 2.x, a maioria das pessoas parece estar usando uma instrução if com duas verificações. um para str e outro para unicode.
Se você quiser verificar se possui um objeto 'semelhante a uma string', com apenas uma instrução, faça o seguinte:
isinstance(x, basestring)
isinstance(u"x",basestring)
retorna True
.
Unicode não é uma codificação - para citar Kumar McMillan:
Se ASCII, UTF-8 e outras cadeias de bytes são "texto" ...
... então Unicode é "text-ness";
é a forma abstrata do texto
Leia a palestra Unicode In Python, completamente desmistificada do PyCon 2008, da McMillan, que explica as coisas muito melhor do que a maioria das respostas relacionadas ao Stack Overflow.
Se as suas necessidades de código para ser compatível com ambos Python 2 e Python 3, você não pode usar diretamente coisas como isinstance(s,bytes)
ou isinstance(s,unicode)
sem colocá-los em qualquer try / exceto ou um teste de versão python, porque bytes
é indefinido em Python 2 e unicode
é indefinido em Python 3 .
Existem algumas soluções feias. Uma coisa extremamente feia é comparar o nome do tipo, em vez de comparar o próprio tipo. Aqui está um exemplo:
# convert bytes (python 3) or unicode (python 2) to str
if str(type(s)) == "<class 'bytes'>":
# only possible in Python 3
s = s.decode('ascii') # or s = str(s)[2:-1]
elif str(type(s)) == "<type 'unicode'>":
# only possible in Python 2
s = str(s)
Uma solução indiscutivelmente um pouco menos feia é verificar o número da versão do Python, por exemplo:
if sys.version_info >= (3,0,0):
# for Python 3
if isinstance(s, bytes):
s = s.decode('ascii') # or s = str(s)[2:-1]
else:
# for Python 2
if isinstance(s, unicode):
s = str(s)
Ambos são não-tônicos, e na maioria das vezes provavelmente existe uma maneira melhor.
six
e testar contra six.binary_type
esix.text_type
usar:
import six
if isinstance(obj, six.text_type)
dentro da biblioteca seis é representado como:
if PY3:
string_types = str,
else:
string_types = basestring,
if isinstance(obj, six.text_type)
. Mas sim, esta é a resposta correta.
Observe que no Python 3, não é realmente justo dizer um dos seguintes:
str
s são UTFx para qualquer x (por exemplo, UTF8)
str
s são Unicode
str
s são coleções ordenadas de caracteres Unicode
O str
tipo de Python é (normalmente) uma sequência de pontos de código Unicode, alguns dos quais são mapeados para caracteres.
Mesmo no Python 3, não é tão simples responder a essa pergunta como você pode imaginar.
Uma maneira óbvia de testar cadeias compatíveis com ASCII é através de uma tentativa de codificação:
"Hello there!".encode("ascii")
#>>> b'Hello there!'
"Hello there... ☃!".encode("ascii")
#>>> Traceback (most recent call last):
#>>> File "", line 4, in <module>
#>>> UnicodeEncodeError: 'ascii' codec can't encode character '\u2603' in position 15: ordinal not in range(128)
O erro distingue os casos.
No Python 3, existem até algumas strings que contêm pontos de código Unicode inválidos:
"Hello there!".encode("utf8")
#>>> b'Hello there!'
"\udcc3".encode("utf8")
#>>> Traceback (most recent call last):
#>>> File "", line 19, in <module>
#>>> UnicodeEncodeError: 'utf-8' codec can't encode character '\udcc3' in position 0: surrogates not allowed
O mesmo método para distingui-los é usado.
Isso pode ajudar outra pessoa. Comecei a testar o tipo de string da variável s, mas, para meu aplicativo, fazia mais sentido simplesmente retornar s como utf-8. O processo que chama return_utf, então sabe com o que está lidando e pode manipular a sequência adequadamente. O código não é puro, mas pretendo que ele seja independente da versão Python sem um teste de versão ou sem importar seis. Comente com melhorias no código de exemplo abaixo para ajudar outras pessoas.
def return_utf(s):
if isinstance(s, str):
return s.encode('utf-8')
if isinstance(s, (int, float, complex)):
return str(s).encode('utf-8')
try:
return s.encode('utf-8')
except TypeError:
try:
return str(s).encode('utf-8')
except AttributeError:
return s
except AttributeError:
return s
return s # assume it was already utf-8
Você pode usar o Universal Encoding Detector , mas lembre-se de que ele fornecerá o melhor palpite, não a codificação real, porque é impossível saber a codificação de uma string "abc", por exemplo. Você precisará obter informações de codificação em outro lugar, por exemplo, o protocolo HTTP usa o cabeçalho Content-Type para isso.
Para compatibilidade com py2 / py3, basta usar
import six
if isinstance(obj, six.text_type)
Uma abordagem simples é verificar se unicode
é uma função interna. Nesse caso, você está no Python 2 e sua string será uma string. Para garantir que tudo está em unicode
um pode fazer:
import builtins
i = 'cats'
if 'unicode' in dir(builtins): # True in python 2, False in 3
i = unicode(i)