tl; dr / quick fix
- Ne pas décoder / encoder bon gré mal gré
- Ne présumez pas que vos chaînes sont encodées en UTF-8
- Essayez de convertir les chaînes en chaînes Unicode dès que possible dans votre code
- Fixez vos paramètres régionaux: Comment résoudre UnicodeDecodeError en Python 3.6?
- Ne soyez pas tenté d'utiliser des
reload
hacks rapides
Unicode Zen dans Python 2.x - La version longue
Sans voir la source, il est difficile de connaître la cause profonde, je vais donc devoir parler de manière générale.
UnicodeDecodeError: 'ascii' codec can't decode byte
se produit généralement lorsque vous essayez de convertir un Python 2.x str
qui contient non-ASCII en une chaîne Unicode sans spécifier le codage de la chaîne d'origine.
En bref, les chaînes Unicode sont un type de chaîne Python entièrement distinct qui ne contient aucun encodage. Ils ne contiennent que des codes de point Unicode et peuvent donc contenir n'importe quel point Unicode de l'ensemble du spectre. Les chaînes contiennent du texte codé, beit UTF-8, UTF-16, ISO-8895-1, GBK, Big5 etc. Les chaînes sont décodées en Unicode et les Unicodes sont codés en chaînes . Les fichiers et les données texte sont toujours transférés dans des chaînes codées.
Les auteurs du module Markdown utilisent probablement unicode()
(là où l'exception est levée) comme une porte de qualité vers le reste du code - il convertira ASCII ou ré-emballera les chaînes Unicodes existantes en une nouvelle chaîne Unicode. Les auteurs de Markdown ne peuvent pas connaître l'encodage de la chaîne entrante, ils comptent donc sur vous pour décoder les chaînes en chaînes Unicode avant de passer à Markdown.
Les chaînes Unicode peuvent être déclarées dans votre code en utilisant le u
préfixe des chaînes. Par exemple
>>> my_u = u'my ünicôdé strįng'
>>> type(my_u)
<type 'unicode'>
Les chaînes Unicode peuvent également provenir de fichiers, de bases de données et de modules réseau. Lorsque cela se produit, vous n'avez pas à vous soucier de l'encodage.
Gotchas
La conversion de str
à Unicode peut se produire même lorsque vous n'appelez pas explicitement unicode()
.
Les scénarios suivants provoquent des UnicodeDecodeError
exceptions:
# Explicit conversion without encoding
unicode('€')
# New style format string into Unicode string
# Python will try to convert value string to Unicode first
u"The currency is: {}".format('€')
# Old style format string into Unicode string
# Python will try to convert value string to Unicode first
u'The currency is: %s' % '€'
# Append string to Unicode
# Python will try to convert string to Unicode first
u'The currency is: ' + '€'
Exemples
Dans le diagramme suivant, vous pouvez voir comment le mot café
a été encodé en encodage "UTF-8" ou "Cp1252" en fonction du type de terminal. Dans les deux exemples, caf
c'est juste un ascii régulier. En UTF-8, é
est codé en utilisant deux octets. Dans "Cp1252", é est 0xE9 (qui est également la valeur du point Unicode (ce n'est pas un hasard)). Le correct decode()
est invoqué et la conversion en Unicode Python est réussie:
Dans ce diagramme, decode()
est appelé avec ascii
(ce qui revient à appeler unicode()
sans codage donné). Comme ASCII ne peut pas contenir d'octets supérieurs à 0x7F
, cela lèvera une UnicodeDecodeError
exception:
Le sandwich Unicode
Il est recommandé de former un sandwich Unicode dans votre code, où vous décodez toutes les données entrantes en chaînes Unicode, travaillez avec Unicodes, puis encodez en str
s à la sortie. Cela vous évite de vous soucier du codage des chaînes au milieu de votre code.
Entrée / décodage
Code source
Si vous avez besoin de créer des chaînes non ASCII dans votre code source, créez simplement des chaînes Unicode en préfixant la chaîne avec un u
. Par exemple
u'Zürich'
Pour permettre à Python de décoder votre code source, vous devrez ajouter un en-tête de codage pour correspondre au codage réel de votre fichier. Par exemple, si votre fichier a été encodé en 'UTF-8', vous utiliseriez:
# encoding: utf-8
Cela n'est nécessaire que si vous avez un code non ASCII dans votre code source .
Des dossiers
Habituellement, les données non ASCII sont reçues d'un fichier. Le io
module fournit un TextWrapper qui décode votre fichier à la volée, en utilisant une donnée encoding
. Vous devez utiliser l'encodage correct pour le fichier - il ne peut pas être facilement deviné. Par exemple, pour un fichier UTF-8:
import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
my_unicode_string = my_file.read()
my_unicode_string
serait alors adapté pour passer à Markdown. Si un UnicodeDecodeError
de la read()
ligne, alors vous avez probablement utilisé la mauvaise valeur de codage.
Fichiers CSV
Le module Python 2.7 CSV ne prend pas en charge les caractères non ASCII 😩. Cependant, l'aide est à portée de main avec https://pypi.python.org/pypi/backports.csv .
Utilisez-le comme ci-dessus mais passez-lui le fichier ouvert:
from backports import csv
import io
with io.open("my_utf8_file.txt", "r", encoding="utf-8") as my_file:
for row in csv.reader(my_file):
yield row
Bases de données
La plupart des pilotes de base de données Python peuvent renvoyer des données en Unicode, mais nécessitent généralement un peu de configuration. Utilisez toujours des chaînes Unicode pour les requêtes SQL.
MySQL
Dans la chaîne de connexion, ajoutez:
charset='utf8',
use_unicode=True
Par exemple
>>> db = MySQLdb.connect(host="localhost", user='root', passwd='passwd', db='sandbox', use_unicode=True, charset="utf8")
PostgreSQL
Ajouter:
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY)
HTTP
Les pages Web peuvent être encodées dans à peu près n'importe quel encodage. L'en- Content-type
tête doit contenir un charset
champ pour indiquer l'encodage. Le contenu peut ensuite être décodé manuellement par rapport à cette valeur. Alternativement, Python-Requests renvoie Unicodes dans response.text
.
Manuellement
Si vous devez décoder les chaînes manuellement, vous pouvez simplement le faire my_string.decode(encoding)
, où encoding
est l'encodage approprié. Les codecs pris en charge par Python 2.x sont indiqués ici: Codages standard . Encore une fois, si vous obtenez, UnicodeDecodeError
vous avez probablement un mauvais codage.
La viande du sandwich
Travaillez avec Unicodes comme vous le feriez avec des chaînes normales.
Production
sortie standard / impression
print
écrit via le flux stdout. Python essaie de configurer un encodeur sur stdout afin que les Unicodes soient encodés selon l'encodage de la console. Par exemple, si un shell Linux l' locale
est en_GB.UTF-8
, la sortie sera encodée en UTF-8
. Sous Windows, vous serez limité à une page de codes 8 bits.
Une console mal configurée, comme des paramètres régionaux corrompus, peut entraîner des erreurs d'impression inattendues. PYTHONIOENCODING
La variable d'environnement peut forcer l'encodage pour stdout.
Des dossiers
Tout comme l'entrée, io.open
peut être utilisé pour convertir de manière transparente des Unicodes en chaînes d'octets codées.
Base de données
La même configuration de lecture permettra d'écrire directement les Unicodes.
Python 3
Python 3 n'est pas plus compatible Unicode que Python 2.x, mais il est légèrement moins confus sur le sujet. Par exemple, le régulier str
est maintenant une chaîne Unicode et l'ancien l' str
est maintenant bytes
.
L'encodage par défaut est UTF-8, donc si vous .decode()
une chaîne d'octets sans donner d'encodage, Python 3 utilise l'encodage UTF-8. Cela résout probablement 50% des problèmes Unicode des utilisateurs.
De plus, open()
fonctionne en mode texte par défaut, donc retourne décodé str
(ceux Unicode). L'encodage est dérivé de votre environnement local, qui a tendance à être UTF-8 sur les systèmes Un * x ou une page de code 8 bits, telle que windows-1251, sur les boîtes Windows.
Pourquoi vous ne devriez pas utiliser sys.setdefaultencoding('utf8')
C'est un hack méchant (il y a une raison que vous devez utiliser reload
) qui ne fera que masquer les problèmes et gêner votre migration vers Python 3.x. Comprenez le problème, corrigez la cause première et profitez d'Unicode zen. Voir Pourquoi ne devrions-nous PAS utiliser sys.setdefaultencoding ("utf-8") dans un script py? pour plus de détails