Cosa devo fare in Python per capire quale codifica ha una stringa?
{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
Cosa devo fare in Python per capire quale codifica ha una stringa?
{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
Risposte:
In Python 3, tutte le stringhe sono sequenze di caratteri Unicode. Esiste un bytes
tipo che contiene byte non elaborati.
In Python 2, una stringa può essere di tipo str
o di tipo unicode
. Puoi dire quale codice usando qualcosa del genere:
def whatisthis(s):
if isinstance(s, str):
print "ordinary string"
elif isinstance(s, unicode):
print "unicode string"
else:
print "not a string"
Questo non distingue "Unicode o ASCII"; distingue solo i tipi di Python. Una stringa Unicode può essere composta esclusivamente da caratteri nell'intervallo ASCII e una stringa secondaria può contenere ASCII, Unicode codificato o persino dati non testuali.
Puoi usare type
o isinstance
.
In Python 2:
>>> type(u'abc') # Python 2 unicode string literal
<type 'unicode'>
>>> type('abc') # Python 2 byte string literal
<type 'str'>
In Python 2, str
è solo una sequenza di byte. Python non sa quale sia la sua codifica. Il unicode
tipo è il modo più sicuro per memorizzare il testo. Se vuoi capirlo di più, ti consiglio http://farmdev.com/talks/unicode/ .
In Python 3:
>>> type('abc') # Python 3 unicode string literal
<class 'str'>
>>> type(b'abc') # Python 3 byte string literal
<class 'bytes'>
In Python 3, str
è come Python 2 unicode
e viene utilizzato per memorizzare il testo. Quello che è stato chiamato str
in Python 2 è chiamato bytes
in Python 3.
Puoi chiamare decode
. Se genera un'eccezione UnicodeDecodeError, non era valido.
>>> 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")
o qualcosa del genere
str(s, "ascii")
In Python 3.x tutte le stringhe sono sequenze di caratteri Unicode. e fare il controllo isinstance per str (che significa stringa unicode di default) dovrebbe essere sufficiente.
isinstance(x, str)
Per quanto riguarda Python 2.x, la maggior parte delle persone sembra utilizzare un'istruzione if con due controlli. uno per str e uno per unicode.
Se vuoi verificare se hai un oggetto "simile a una stringa", tutto con un'istruzione, puoi fare quanto segue:
isinstance(x, basestring)
isinstance(u"x",basestring)
ritorna True
.
Unicode non è una codifica - per citare Kumar McMillan:
Se ASCII, UTF-8 e altre stringhe di byte sono "testo" ...
... quindi Unicode è "text-ness";
è la forma astratta del testo
Leggi l' Unicode di McMillan in Python, Discorso completamente demistificato di PyCon 2008, spiega le cose molto meglio della maggior parte delle risposte correlate su Stack Overflow.
Se le vostre esigenze di codice per essere compatibili con entrambi Python 2 e Python 3, non è possibile utilizzare direttamente le cose come isinstance(s,bytes)
o isinstance(s,unicode)
senza avvolgendoli in entrambi i try / tranne o di un test versione di Python, perché bytes
non è definito in Python 2 e unicode
non è definito in Python 3 .
Ci sono alcune brutte soluzioni alternative. Uno estremamente brutto è quello di confrontare il nome del tipo, invece di confrontare il tipo stesso. Ecco un esempio:
# 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)
Una soluzione leggermente meno brutta è verificare il numero di versione di Python, ad esempio:
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)
Sono entrambi non-ritonici e la maggior parte delle volte c'è probabilmente un modo migliore.
six
e testare contro six.binary_type
esix.text_type
uso:
import six
if isinstance(obj, six.text_type)
all'interno delle sei librerie è rappresentato come:
if PY3:
string_types = str,
else:
string_types = basestring,
if isinstance(obj, six.text_type)
. Ma sì, questa è la risposta corretta.
Nota che su Python 3 non è proprio corretto dire:
str
s sono UTFx per qualsiasi x (es. UTF8)
str
s sono Unicode
str
sono raccolte ordinate di caratteri Unicode
Il str
tipo di Python è (normalmente) una sequenza di punti di codice Unicode, alcuni dei quali associati a caratteri.
Anche su Python 3, non è così semplice rispondere a questa domanda come potresti immaginare.
Un modo ovvio per testare stringhe compatibili ASCII è tramite un tentativo di codifica:
"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)
L'errore distingue i casi.
In Python 3, ci sono anche alcune stringhe che contengono punti di codice Unicode non validi:
"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
Viene utilizzato lo stesso metodo per distinguerli.
Questo può aiutare qualcun altro, ho iniziato a testare il tipo di stringa della variabile s, ma per la mia applicazione, aveva più senso restituire semplicemente s come utf-8. Il processo che chiama return_utf, quindi sa con cosa ha a che fare e può gestire la stringa in modo appropriato. Il codice non è incontaminato, ma intendo che sia agnostico in versione Python senza test di versione o importazione di sei. Si prega di commentare con miglioramenti al codice di esempio riportato di seguito per aiutare altre persone.
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
Potresti usare Universal Encoding Detector , ma tieni presente che ti darà solo la migliore ipotesi, non la codifica effettiva, perché è impossibile conoscere la codifica di una stringa "abc" per esempio. Dovrai ottenere informazioni di codifica altrove, ad esempio il protocollo HTTP utilizza l'intestazione Content-Type per quello.
Per la compatibilità con py2 / py3, utilizzare semplicemente
import six
if isinstance(obj, six.text_type)
Un approccio semplice è verificare se unicode
è una funzione integrata. In tal caso, sei in Python 2 e la tua stringa sarà una stringa. Per assicurarti che tutto sia in unicode
uno, puoi fare:
import builtins
i = 'cats'
if 'unicode' in dir(builtins): # True in python 2, False in 3
i = unicode(i)