Come posso verificare se una stringa è Unicode o ASCII?


271

Cosa devo fare in Python per capire quale codifica ha una stringa?


56
Unicode non è una codifica.
Ulidtko,

Ancora più importante, perché dovrebbe interessarti?
Johnsyweb,

@Johnsyweb A causa di{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
alex

Risposte:


295

In Python 3, tutte le stringhe sono sequenze di caratteri Unicode. Esiste un bytestipo che contiene byte non elaborati.

In Python 2, una stringa può essere di tipo stro 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.


3
@ProsperousHeart: Probabilmente stai usando Python 3.
Greg Hewgill

124

Come stabilire se un oggetto è una stringa unicode o una stringa di byte

Puoi usare typeo 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 unicodetipo è 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 unicodee viene utilizzato per memorizzare il testo. Quello che è stato chiamato strin Python 2 è chiamato bytesin Python 3.


Come sapere se una stringa di byte è valida utf-8 o ascii

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)

Solo per riferimento di altre persone - str.decode non esiste in Python 3. Sembra che tu debba unicode(s, "ascii")o qualcosa del genere
Shadow

3
Mi dispiace, intendevostr(s, "ascii")
Shadow

1
Questo non è preciso per Python 3
ProsperousHeart

2
@ProsperousHeart Aggiornato per includere Python 3. E per provare a spiegare la differenza tra bytestring e stringhe unicode.
Mikel,

44

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)

Questo è falso In Python 2.7 isinstance(u"x",basestring)ritorna True.
PythonNut

11
@PythonNut: credo che fosse questo il punto. L'uso di isinstance (x, basestring) è sufficiente per sostituire i doppi test distinti sopra.
KQ.

5
È utile in molti casi, ma evidentemente non è quello che intendeva l'interrogante.
mhsmith,

3
Questa è la risposta alla domanda. Tutti gli altri hanno frainteso ciò che ha detto OP e hanno dato risposte generiche sul controllo dei tipi in Python.
fiatjaf,

1
Non risponde alla domanda di OP. Il titolo della domanda (da solo) POTREBBE essere interpretato in modo tale che questa risposta sia corretta. Tuttavia, OP specifica nello specifico "capire quale" nella descrizione della domanda, e questa risposta non risolve questo problema.
MD004,

31

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.


Quelle diapositive sono probabilmente la migliore introduzione a Unicode in cui mi sono imbattuto finora
Jonny,

23

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é bytesnon è definito in Python 2 e unicodenon è 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.


6
Il modo migliore è probabilmente quello di usare sixe testare contro six.binary_typeesix.text_type
Ian Clelland il

1
È possibile utilizzare i tipi .__ nome__ per sondare i nomi dei tipi.
Paulo Freitas,

Non sono del tutto sicuro del caso d'uso di quel bit di codice, a meno che non ci sia un errore logico. Penso che ci dovrebbe essere un "non" nel codice di Python 2. Altrimenti stai convertendo tutto in stringhe unicode per Python 3 e il contrario per Python 2!
Oligofren,

Sì, oligofren, ecco cosa fa. Le stringhe interne standard sono Unicode in Python 3 e ASCII in Python 2. Quindi gli snippet di codice convertono il testo in tipo di stringa interna standard (sia esso Unicode o ASCII).
Dave Burton,

12

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,

2
dovrebbe essere if isinstance(obj, six.text_type) . Ma sì, questa è la risposta corretta.
Karantan,

Non risponde alla domanda di OP. Il titolo della domanda (da solo) POTREBBE essere interpretato in modo tale che questa risposta sia corretta. Tuttavia, OP specifica nello specifico "capire quale" nella descrizione della domanda, e questa risposta non risolve questo problema.
MD004,

4

Nota che su Python 3 non è proprio corretto dire:

  • strs sono UTFx per qualsiasi x (es. UTF8)

  • strs sono Unicode

  • strsono raccolte ordinate di caratteri Unicode

Il strtipo 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.


3

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

Tu amico mio meriti di essere la risposta corretta! Sto usando Python 3 e ho avuto ancora problemi fino a quando non ho trovato questo tesoro!
mnsr,

2

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.


0

Per la compatibilità con py2 / py3, utilizzare semplicemente

import six if isinstance(obj, six.text_type)


0

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 unicodeuno, puoi fare:

import builtins

i = 'cats'
if 'unicode' in dir(builtins):     # True in python 2, False in 3
  i = unicode(i)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.