encodage et décodage de chaînes?

Voici mes tentatives avec des messages d'erreur. Ce que je fais mal?

string.decode("ascii", "ignore")

UnicodeEncodeError: 'ascii' codec can't encode character u'xa0' in position 37: ordinal not in range(128)

string.encode('utf-8', "ignore")

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 37: ordinal not in range(128)

50
demandé sur martineau 2012-07-05 11:48:06

4 réponses

Vous ne pouvez pas décoder un unicode, et vous ne pouvez pas encoder un str. Essayez de le faire dans l'autre sens.

82
répondu Ignacio Vazquez-Abrams 2012-07-05 07:50:15

Deviner toutes les choses omises de la question originale, mais, en supposant Python 2.x la clé est de lire attentivement les messages d'erreur: en particulier lorsque vous appelez 'encoder' mais que le message dit 'Décoder' et vice versa, mais aussi les types des valeurs incluses dans les messages.

Dans le premier exemple string est de type unicode et vous avez tenté de le décoder qui est une opération convertissant une chaîne d'octets en unicode. Python a utilement tenté de convertir la valeur unicode à str en utilisant l'encodage 'ASCII' par défaut mais puisque votre chaîne contenait un caractère non-ascii, vous avez l'erreur qui dit que Python n'a pas puencoder une valeurunicode . Voici un exemple qui montre le type de la chaîne d'entrée:

>>> u"\xa0".decode("ascii", "ignore")

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    u"\xa0".decode("ascii", "ignore")
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 0: ordinal not in range(128)

Dans le second cas, vous faites l'inverse en essayant d'encoder une chaîne d'octets. L'encodage est une opération qui convertit unicode en une chaîne d'octets donc Python tente utilement de convertir votre chaîne d'octets en unicode en premier et, puisque vous ne lui avez pas donné de chaîne ascii, le décodeur ASCII par défaut échoue:

>>> "\xc2".encode("ascii", "ignore")

Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    "\xc2".encode("ascii", "ignore")
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128)
59
répondu Duncan 2012-07-05 11:02:38

En plus d'obtenir decode et encode en arrière, je pense qu'une partie de la réponse ici est en fait n'utilisez pas l'encodage ascii . Ce n'est probablement pas ce que vous voulez.

Pour commencer, pensez à str comme vous le feriez pour un fichier texte brut. C'est juste un tas d'octets sans encodage réellement attaché. Comment il est interprété est à la hauteur de n'importe quel morceau de code le lit. Si vous ne savez pas de quoi parle ce paragraphe, allez lire Le Minimum absolu de Joel chaque Développeur de logiciels absolument, positivement doit connaître Unicode et jeux de caractères dès maintenant avant d'aller plus loin.

Naturellement, nous sommes tous conscients du désordre qui a créé. La réponse est, au moins dans la mémoire, d'avoir un codage standard pour toutes les chaînes. C'est là que unicode entre en jeu. J'ai du mal à suivre exactement ce que Python utilise en interne, mais cela n'a pas vraiment d'importance juste pour cela. Le point est que vous savez que c'est une séquence d'octets sont interprétés d'une certaine manière. Il vous suffit donc de penser aux caractères eux-mêmes, et non aux octets.

Le problème est que, dans la pratique, vous rencontrez les deux. Certaines bibliothèques vous donnent un str, et certaines attendent un str. Certainement, cela a du sens chaque fois que vous diffusez une série d'octets (comme vers ou depuis le disque ou sur une requête web). Donc, vous devez être capable de traduire d'avant en arrière.

Entrer codecs: c'est la bibliothèque de traduction entre ces deux types de données. Vous utilisez encode pour générer une séquence d'octets (str) à partir d'une chaîne de texte (unicode), et que vous utilisez decode pour obtenir une chaîne de texte (unicode) à partir d'une séquence d'octets (str).

Par exemple:

>>> s = "I look like a string, but I'm actually a sequence of bytes. \xe2\x9d\xa4"
>>> codecs.decode(s, 'utf-8')
u"I look like a string, but I'm actually a sequence of bytes. \u2764"

Ce qui s'est passé ici? J'ai donné à Python une séquence d'octets, puis je lui ai dit: "Donnez-moi la version unicode de ceci, étant donné que cette séquence d'octets est dans 'utf-8'."Il a fait comme je l'ai demandé, et ces octets ( un caractère de coeur ) sont maintenant traités comme un tout, représenté par leur Unicode codepoint.

Allons dans l'autre sens:

>>> u = u"I'm a string! Really! \u2764"
>>> codecs.encode(u, 'utf-8')
"I'm a string! Really! \xe2\x9d\xa4"

J'ai donné à Python une chaîne Unicode, et je lui ai demandé de traduire la chaîne en une séquence d'octets en utilisant le codage 'utf-8'. Donc, il l'a fait, et maintenant le cœur est juste un tas d'octets qu'il ne peut pas imprimer en ASCII; donc il me montre l'hexadécimal à la place.

Nous pouvons travailler avec d'autres encodages, aussi, bien sûr:

>>> s = "I have a section \xa7"
>>> codecs.decode(s, 'latin1')
u'I have a section \xa7'
>>> codecs.decode(s, 'latin1')[-1] == u'\u00A7'
True

>>> u = u"I have a section \u00a7"
>>> u
u'I have a section \xa7'
>>> codecs.encode(u, 'latin1')
'I have a section \xa7'

('\xa7' est le caractère de section , dans les deux Unicode et Latin-1.)

Donc pour votre question, vous devez d'abord comprendre dans quel encodage votre str est.

  • Ça vient d'un fichier? À partir d'une requête web? À partir de votre base de données? Puis la source détermine l'encodage. Découvrez l'encodage de la source et utilisez-le pour le traduire en unicode.

    s = [get from external source]
    u = codecs.decode(s, 'utf-8') # Replace utf-8 with the actual input encoding
    
  • Ou peut-être que vous essayez de l'écrire quelque part. Quel encodage attend la destination? Utilisez-le pour le traduire en str. UTF-8 est un bon choix pour le texte brut documents; la plupart des choses peuvent le lire.

    u = u'My string'
    s = codecs.encode(u, 'utf-8') # Replace utf-8 with the actual output encoding
    [Write s out somewhere]
    
  • Traduisez-vous simplement en arrière et en arrière dans la mémoire pour l'interopérabilité ou quelque chose? Ensuite, choisissez simplement un encodage et respectez - le; 'utf-8' est probablement le meilleur choix pour cela:

    u = u'My string'
    s = codecs.encode(u, 'utf-8')
    newu = codecs.decode(s, 'utf-8')
    

Dans la programmation moderne, vous ne voulez probablement jamais utiliser l'encodage 'ascii' pour tout cela. C'est un sous-ensemble extrêmement petit de tous les caractères possibles, et aucun système que je connaisse ne l'utilise par défaut ou quoi.

Python 3 fait de son mieux pour rendre ce immensément plus clair simplement en changeant les noms. En Python 3, str a été remplacé par bytes, et unicode a été remplacé par str.

24
répondu jpmc26 2015-06-04 04:21:55

C'est parce que votre chaîne d'entrée ne peut pas être convertie selon les règles d'encodage (strictes par défaut).

Je ne sais pas, mais j'ai toujours codé en utilisant directement le constructeur unicode (), au moins c'est le chemin à la documentation officielle :

unicode(your_str, errors="ignore")
2
répondu wikier 2012-07-05 07:58:02