Comment vérifier si une chaîne est unicode ou ascii?

271

Que dois-je faire en Python pour savoir quel encodage a une chaîne?

TIMEX
la source
56
Unicode n'est pas un encodage.
ulidtko
Plus important encore, pourquoi devriez-vous vous en soucier?
Johnsyweb
@Johnsyweb En raison de{UnicodeDecodeError} 'ascii' codec can't decode byte 0xc2
alex

Réponses:

295

En Python 3, toutes les chaînes sont des séquences de caractères Unicode. Il existe un bytestype qui contient des octets bruts.

En Python 2, une chaîne peut être de type strou de type unicode. Vous pouvez dire quel code quelque chose comme ceci:

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

Cela ne distingue pas "Unicode ou ASCII"; il distingue uniquement les types Python. Une chaîne Unicode peut être constituée uniquement de caractères dans la plage ASCII, et un sous-test peut contenir des données ASCII, Unicode codées ou même des données non textuelles.

Greg Hewgill
la source
3
@ProsperousHeart: Vous utilisez probablement Python 3.
Greg Hewgill
124

Comment savoir si un objet est une chaîne unicode ou une chaîne d'octets

Vous pouvez utiliser typeou isinstance.

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, strc'est juste une séquence d'octets. Python ne sait pas quel est son encodage. Le unicodetype est le moyen le plus sûr de stocker du texte. Si vous voulez mieux comprendre cela, je recommande 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, strc'est comme Python 2 unicodeet est utilisé pour stocker du texte. Ce qui était appelé stren Python 2 est appelé bytesen Python 3.


Comment savoir si une chaîne d'octets est valide utf-8 ou ascii

Vous pouvez appeler decode. S'il déclenche une exception UnicodeDecodeError, il n'est pas valide.

>>> 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
la source
Juste pour la référence des autres - str.decode n'existe pas en python 3. On dirait que vous devez unicode(s, "ascii")ou quelque chose
Shadow
3
Désolé, je voulais direstr(s, "ascii")
Shadow
1
Ce n'est pas exact pour python 3
ProsperousHeart
2
@ProsperousHeart Mis à jour pour couvrir Python 3. Et pour essayer d'expliquer la différence entre les bytestrings et les chaînes unicode.
Mikel
44

En python 3.x, toutes les chaînes sont des séquences de caractères Unicode. et faire la vérification isinstance de str (ce qui signifie une chaîne unicode par défaut) devrait suffire.

isinstance(x, str)

En ce qui concerne python 2.x, la plupart des gens semblent utiliser une instruction if qui comporte deux vérifications. un pour str et un pour unicode.

Si vous souhaitez vérifier si vous avez un objet "semblable à une chaîne" avec une seule instruction, vous pouvez effectuer les opérations suivantes:

isinstance(x, basestring)
ThinkBonobo
la source
C'est faux. En Python 2.7 isinstance(u"x",basestring)revient True.
PythonNut
11
@PythonNut: Je crois que c'était le point. L'utilisation de isinstance (x, basestring) suffit pour remplacer les deux tests distincts ci-dessus.
KQ.
5
C'est utile dans de nombreux cas, mais ce n'est évidemment pas ce que le questionneur voulait dire.
mhsmith
3
C'est la réponse à la question. Tous les autres ont mal compris ce qu'OP a dit et ont donné des réponses génériques sur la vérification de type en Python.
fiatjaf
1
Ne répond pas à la question d'OP. Le titre de la question (seul) POURRAIT être interprété de telle sorte que cette réponse soit correcte. Cependant, OP dit spécifiquement «comprendre lequel» dans la description de la question, et cette réponse ne répond pas à cela.
MD004
31

Unicode n'est pas un encodage - pour citer Kumar McMillan:

Si ASCII, UTF-8 et les autres chaînes d'octets sont du "texte" ...

... alors Unicode est "text-ness";

c'est la forme abstraite du texte

Lisez Unicode In Python de McMillan , un discours complètement démystifié de PyCon 2008, il explique les choses beaucoup mieux que la plupart des réponses connexes sur Stack Overflow.

Alex Dean
la source
Ces diapositives sont probablement la meilleure introduction à Unicode que j'ai rencontrée à ce jour
Jonny
23

Si vos besoins de code pour être compatibles avec les deux Python 2 et Python 3, vous ne pouvez pas utiliser directement des choses comme isinstance(s,bytes)ou isinstance(s,unicode)sans les envelopper dans les deux try / except ou un test de la version python, parce que bytesn'est pas défini en Python 2 et unicodeest indéfini en Python 3 .

Il existe des solutions de contournement laides. Un très laid est de comparer le nom du type, au lieu de comparer le type lui-même. Voici un exemple:

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

Une solution de contournement sans doute un peu moins laide consiste à vérifier le numéro de version de Python, par exemple:

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)

Ce sont tous deux impythoniques, et la plupart du temps, il y a probablement une meilleure façon.

Dave Burton
la source
6
La meilleure façon est probablement d'utiliser sixet de tester contre six.binary_typeetsix.text_type
Ian Clelland
1
Vous pouvez utiliser des types .__ nom__ pour sonder les noms de type.
Paulo Freitas
Je ne suis pas tout à fait sûr du cas d'utilisation de ce morceau de code, sauf en cas d'erreur logique. Je pense qu'il devrait y avoir un "non" dans le code python 2. Sinon, vous convertissez tout en chaînes unicode pour Python 3 et l'inverse pour Python 2!
oligofren
Oui, oligofren, c'est ce qu'il fait. Les chaînes internes standard sont Unicode en Python 3 et ASCII en Python 2. Les extraits de code convertissent donc le texte en type de chaîne interne standard (que ce soit Unicode ou ASCII).
Dave Burton
12

utilisation:

import six
if isinstance(obj, six.text_type)

à l'intérieur de la bibliothèque six, il est représenté comme:

if PY3:
    string_types = str,
else:
    string_types = basestring,
madjardi
la source
2
ça devrait l'être if isinstance(obj, six.text_type) . Mais oui, c'est la bonne réponse.
karantan
Ne répond pas à la question d'OP. Le titre de la question (seul) POURRAIT être interprété de telle sorte que cette réponse soit correcte. Cependant, OP dit spécifiquement «comprendre lequel» dans la description de la question, et cette réponse ne répond pas à cela.
MD004
4

Notez que sur Python 3, il n'est pas vraiment juste de dire:

  • strs sont UTFx pour tout x (par exemple UTF8)

  • strs sont Unicode

  • strs sont des collections ordonnées de caractères Unicode

Le strtype de Python est (normalement) une séquence de points de code Unicode, dont certains sont mappés sur des caractères.


Même sur Python 3, il n'est pas aussi simple de répondre à cette question que vous pourriez l'imaginer.

Une façon évidente de tester les chaînes compatibles ASCII est par une tentative de codage:

"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'erreur distingue les cas.

En Python 3, certaines chaînes contiennent même des points de code Unicode non valides:

"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

La même méthode pour les distinguer est utilisée.

Veedrac
la source
3

Cela peut aider quelqu'un d'autre, j'ai commencé à tester le type de chaîne de la variable s, mais pour mon application, il était plus logique de renvoyer simplement s comme utf-8. Le processus appelant return_utf sait alors de quoi il s'agit et peut gérer la chaîne de manière appropriée. Le code n'est pas vierge, mais j'ai l'intention qu'il soit agnostique en version Python sans test de version ni importation de six. Veuillez commenter les améliorations apportées à l'exemple de code ci-dessous pour aider d'autres personnes.

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
la source
Vous mon ami méritez d'être la bonne réponse! J'utilise python 3 et j'avais toujours des problèmes jusqu'à ce que je trouve ce trésor!
mnsr
2

Vous pouvez utiliser Universal Encoding Detector , mais sachez qu'il ne vous donnera que la meilleure estimation, pas l'encodage réel, car il est impossible de connaître l'encodage d'une chaîne "abc" par exemple. Vous devrez obtenir des informations d'encodage ailleurs, par exemple, le protocole HTTP utilise l'en-tête Content-Type pour cela.

Seb
la source
0

Pour la compatibilité py2 / py3, utilisez simplement

import six if isinstance(obj, six.text_type)

Vishvajit Pathak
la source
0

Une approche simple consiste à vérifier s'il unicodes'agit d'une fonction intégrée. Si c'est le cas, vous êtes en Python 2 et votre chaîne sera une chaîne. Pour vous assurer que tout est en unicodeun, vous pouvez:

import builtins

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