¿Cómo verifico si una cadena es unicode o ascii?

271

¿Qué tengo que hacer en Python para descubrir qué codificación tiene una cadena?

TIMEX
fuente
56
Unicode no es una codificación.
ulidtko
Más importante aún, ¿por qué debería importarle?
Johnsyweb
@Johnsyweb Debido a{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
alex

Respuestas:

295

En Python 3, todas las cadenas son secuencias de caracteres Unicode. Hay un bytestipo que contiene bytes sin procesar.

En Python 2, una cadena puede ser de tipo stro de tipo unicode. Puedes decir qué código usando algo como esto:

def whatisthis(s):
    if isinstance(s, str):
        print "ordinary string"
    elif isinstance(s, unicode):
        print "unicode string"
    else:
        print "not a string"

Esto no distingue "Unicode o ASCII"; solo distingue los tipos de Python. Una cadena Unicode puede consistir en puramente caracteres en el rango ASCII, y una cadena de bytes puede contener ASCII, Unicode codificado o incluso datos no textuales.

Greg Hewgill
fuente
3
@ProsperousHeart: Probablemente estés usando Python 3.
Greg Hewgill
124

Cómo saber si un objeto es una cadena Unicode o una cadena de bytes

Puedes usar typeoisinstance .

En Python 2:

>>> type(u'abc')  # Python 2 unicode string literal
<type 'unicode'>
>>> type('abc')   # Python 2 byte string literal
<type 'str'>

En Python 2, stres solo una secuencia de bytes. Python no sabe cuál es su codificación. El unicodetipo es la forma más segura de almacenar texto. Si quieres entender esto más, te recomiendo http://farmdev.com/talks/unicode/ .

En Python 3:

>>> type('abc')   # Python 3 unicode string literal
<class 'str'>
>>> type(b'abc')  # Python 3 byte string literal
<class 'bytes'>

En Python 3, stres como Python 2 unicodey se usa para almacenar texto. Lo que se llamó stren Python 2 se llama bytesen Python 3.


Cómo saber si una cadena de bytes es válida utf-8 o ascii

Puede llamar decode. Si genera una excepción UnicodeDecodeError, no fue válida.

>>> 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)
Mikel
fuente
Solo para referencia de otras personas: str.decode no existe en python 3. Parece que tienes que hacerlo unicode(s, "ascii")o algo así
Shadow
3
Lo siento, quise decirstr(s, "ascii")
Sombra
1
Esto no es exacto para python 3
ProsperousHeart
2
@ProsperousHeart Actualizado para cubrir Python 3. Y para tratar de explicar la diferencia entre cadenas de bytes y cadenas unicode.
Mikel
44

En python 3.x todas las cadenas son secuencias de caracteres Unicode. y hacer la verificación isinstance para str (que significa cadena unicode por defecto) debería ser suficiente.

isinstance(x, str)

Con respecto a python 2.x, la mayoría de las personas parecen estar usando una declaración if que tiene dos verificaciones. uno para str y otro para unicode.

Sin embargo, si desea verificar si tiene un objeto 'tipo cadena' con una sola declaración, puede hacer lo siguiente:

isinstance(x, basestring)
ThinkBonobo
fuente
Esto es falso En Python 2.7 isinstance(u"x",basestring)regresa True.
PythonNut
11
@PythonNut: Creo que ese era el punto. El uso de isinstance (x, basetring) es suficiente para reemplazar las distintas pruebas duales anteriores.
KQ.
55
Es útil en muchos casos, pero evidentemente no es lo que quiso decir el interrogador.
mhsmith
3
Esta es la respuesta a la pregunta. Todos los demás entendieron mal lo que OP dijo y dieron respuestas genéricas sobre la verificación de tipos en Python.
fiatjaf
1
No responde la pregunta de OP. El título de la pregunta (solo) PODRÍA interpretarse de manera que esta respuesta sea correcta. Sin embargo, OP dice específicamente "averiguar qué" en la descripción de la pregunta, y esta respuesta no aborda eso.
MD004
31

Unicode no es una codificación, para citar a Kumar McMillan:

Si ASCII, UTF-8 y otras cadenas de bytes son "texto" ...

... entonces Unicode es "text-ness";

es la forma abstracta de texto

Lea el Unicode In Python de McMillan , una charla completamente desmitificada de PyCon 2008, que explica las cosas mucho mejor que la mayoría de las respuestas relacionadas en Stack Overflow.

Alex Dean
fuente
Esas diapositivas son probablemente la mejor introducción a Unicode que he encontrado hasta la fecha
Jonny
23

Si su código necesita ser compatible tanto con Python 2 como con Python 3, no puede usar directamente cosas como isinstance(s,bytes)o isinstance(s,unicode)sin envolverlas en try / except o una prueba de versión de python, porquebytes no está definido en Python 2 yunicode no está definido en Python 3 .

Hay algunas soluciones feas. Una muy fea es comparar el nombre del tipo, en lugar de comparar el tipo en sí. Aquí hay un ejemplo:

# 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 solución alternativa un poco menos fea es verificar el número de versión de Python, por ejemplo:

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)

Ambas son poco pitónicas, y la mayoría de las veces probablemente haya una mejor manera.

Dave Burton
fuente
66
La mejor manera es probablemente usar six, y probar contra six.binary_typeysix.text_type
Ian Clelland
1
Puede usar los tipos .__ nombre__ para sondear nombres de tipos.
Paulo Freitas
No estoy muy seguro del caso de uso de ese bit de código, a menos que haya un error lógico. Creo que debería haber un "no" en el código de Python 2. De lo contrario, ¡está convirtiendo todo en cadenas unicode para Python 3 y lo contrario para Python 2!
oligofren
Sí, oligofren, eso es lo que hace. Las cadenas internas estándar son Unicode en Python 3 y ASCII en Python 2. Por lo tanto, los fragmentos de código convierten el texto al tipo de cadena interna estándar (ya sea Unicode o ASCII).
Dave Burton el
12

utilizar:

import six
if isinstance(obj, six.text_type)

dentro de las seis bibliotecas se representa como:

if PY3:
    string_types = str,
else:
    string_types = basestring,
madjardi
fuente
2
debería ser if isinstance(obj, six.text_type) . Pero sí, esta es la respuesta correcta.
karantan
No responde la pregunta de OP. El título de la pregunta (solo) PODRÍA interpretarse de manera que esta respuesta sea correcta. Sin embargo, OP dice específicamente "averiguar qué" en la descripción de la pregunta, y esta respuesta no aborda eso.
MD004
4

Tenga en cuenta que en Python 3, no es realmente justo decir nada de:

  • strs son UTFx para cualquier x (por ejemplo, UTF8)

  • strs son Unicode

  • strs son colecciones ordenadas de caracteres Unicode

El strtipo de Python es (normalmente) una secuencia de puntos de código Unicode, algunos de los cuales se asignan a caracteres.


Incluso en Python 3, no es tan simple responder esta pregunta como te puedas imaginar.

Una forma obvia de probar cadenas compatibles con ASCII es mediante un intento de codificación:

"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)

El error distingue los casos.

En Python 3, incluso hay algunas cadenas que contienen puntos de código Unicode no vá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

Se utiliza el mismo método para distinguirlos.

Veedrac
fuente
3

Esto puede ayudar a alguien más, comencé a probar el tipo de cadena de la variable s, pero para mi aplicación, tenía más sentido simplemente devolver s como utf-8. El proceso que llama a return_utf, luego sabe de qué se trata y puede manejar la cadena de manera apropiada. El código no es prístino, pero tengo la intención de que sea independiente de la versión de Python sin una prueba de versión o importando seis. Comente con las mejoras al código de muestra a continuación para ayudar a otras personas.

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
jfl
fuente
¡Tú, mi amigo, mereces ser la respuesta correcta! ¡Estoy usando Python 3 y todavía tenía problemas hasta que encontré este tesoro!
mnsr
2

Podría usar el Detector de codificación universal , pero tenga en cuenta que solo le dará una mejor suposición, no la codificación real, porque es imposible saber la codificación de una cadena "abc", por ejemplo. Necesitará obtener información de codificación en otro lugar, por ejemplo, el protocolo HTTP utiliza el encabezado Content-Type para eso.

Seb
fuente
0

Para compatibilidad con py2 / py3 simplemente use

import six if isinstance(obj, six.text_type)

Vishvajit Pathak
fuente
0

Un enfoque simple es verificar si unicodees una función incorporada. Si es así, estás en Python 2 y tu cadena será una cadena. Para asegurarse de que todo está en unicodeuno, puede hacer:

import builtins

i = 'cats'
if 'unicode' in dir(builtins):     # True in python 2, False in 3
  i = unicode(i)
duhaime
fuente