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 bytestipo que contém bytes brutos.
No Python 2, uma string pode ser do tipo strou 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 typeou 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 unicodetipo é 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 unicodee é usado para armazenar texto. O que foi chamado strno Python 2 é chamado bytesno 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.
sixe testar contra six.binary_typeesix.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:
strs são UTFx para qualquer x (por exemplo, UTF8)
strs são Unicode
strs são coleções ordenadas de caracteres Unicode
O strtipo 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 unicodeum pode fazer:
import builtins
i = 'cats'
if 'unicode' in dir(builtins): # True in python 2, False in 3
i = unicode(i)