La clé de ces problèmes d'encodage est de comprendre qu'il existe en principe deux concepts distincts de "chaîne" : (1) chaîne de caractères et (2) chaîne / tableau d' octets. Cette distinction a été longtemps ignorée du fait de l'ubiquité historique des encodages ne dépassant pas 256 caractères (ASCII, Latin-1, Windows-1252, Mac OS Roman,…): ces encodages mappent un ensemble de caractères communs à nombres entre 0 et 255 (c'est-à-dire octets); l'échange relativement limité de fichiers avant l'avènement du web rendait tolérable cette situation d'encodages incompatibles, car la plupart des programmes pouvaient ignorer le fait qu'il y avait plusieurs encodages tant qu'ils produisaient du texte qui restait sur le même système d'exploitation: de tels programmes traiter le texte comme des octets (via le codage utilisé par le système d'exploitation). La vue correcte et moderne sépare correctement ces deux concepts de chaîne, en fonction des deux points suivants:
Les personnages sont pour la plupart sans rapport avec les ordinateurs : on peut les dessiner sur un tableau noir, etc., comme par exemple بايثون, 中 蟒 et 🐍. Les «caractères» pour les machines incluent également les «instructions de dessin» comme par exemple les espaces, le retour chariot, les instructions pour définir le sens d'écriture (pour l'arabe, etc.), les accents, etc. Une très grande liste de caractères est incluse dans le standard Unicode ; il couvre la plupart des personnages connus.
D'un autre côté, les ordinateurs doivent représenter des caractères abstraits d'une manière ou d'une autre: pour cela, ils utilisent des tableaux d'octets (nombres compris entre 0 et 255), car leur mémoire est constituée de blocs d'octets. Le processus nécessaire qui convertit les caractères en octets est appelé encodage . Ainsi, un ordinateur nécessite un encodage pour représenter des caractères. Tout texte présent sur votre ordinateur est encodé (jusqu'à ce qu'il soit affiché), qu'il soit envoyé à un terminal (qui attend des caractères encodés d'une manière spécifique), ou enregistré dans un fichier. Afin d'être affichés ou correctement «compris» (par exemple, par l'interpréteur Python), les flux d'octets sont décodés en caractères. Quelques encodages(UTF-8, UTF-16,…) sont définis par Unicode pour sa liste de caractères (Unicode définit donc à la fois une liste de caractères et des encodages pour ces caractères - il y a encore des endroits où l'on voit l'expression «encodage Unicode» comme un façon de faire référence à l'omniprésent UTF-8, mais c'est une terminologie incorrecte, car Unicode fournit plusieurs encodages).
En résumé, les ordinateurs doivent représenter en interne des caractères avec des octets , et ils le font via deux opérations:
Encodage : caractères → octets
Décodage : octets → caractères
Certains encodages ne peuvent pas encoder tous les caractères (par exemple, ASCII), tandis que (certains) encodages Unicode vous permettent d'encoder tous les caractères Unicode. Le codage n'est pas non plus nécessairement unique , car certains caractères peuvent être représentés soit directement, soit sous forme de combinaison (par exemple d'un caractère de base et d'accents).
Notez que le concept de nouvelle ligne ajoute une couche de complication , car il peut être représenté par différents (contrôle) caractères qui dépendent du système d'exploitation (ce qui est la raison de Python en mode lecture de fichier universel newline ).
Maintenant, ce que j'ai appelé « caractère » ci - dessus est ce que Unicode appelle un « caractère perçu par l' utilisateur ». Un seul caractère perçu par l'utilisateur peut parfois être représenté en Unicode en combinant des parties de caractère (caractère de base, accents,…) trouvés à différents index dans la liste Unicode, qui sont appelés " points de code " - ces points de codes peuvent être combinés pour former un "cluster de graphèmes". Unicode conduit ainsi à un troisième concept de chaîne, constitué d'une séquence de points de code Unicode, qui se situe entre des chaînes d'octets et de caractères, et qui est plus proche de ces dernières. Je les appellerai " chaînes Unicode " (comme dans Python 2).
Alors que Python peut imprimer des chaînes de caractères (perçus par l'utilisateur), les chaînes Python non octets sont essentiellement des séquences de points de code Unicode , et non de caractères perçus par l'utilisateur. Les valeurs de point de code sont celles utilisées dans la syntaxe de chaîne Python \u
et \U
Unicode. Ils ne doivent pas être confondus avec le codage d'un caractère (et ne doivent pas avoir de relation avec lui: les points de code Unicode peuvent être codés de différentes manières).
Cela a une conséquence importante: la longueur d'une chaîne Python (Unicode) est son nombre de points de code, qui n'est pas toujours son nombre de caractères perçus par l'utilisateur : ainsi s = "\u1100\u1161\u11a8"; print(s, "len", len(s))
(Python 3) donne 각 len 3
malgré s
avoir un seul utilisateur perçu (coréen) caractère (car il est représenté avec 3 points de code - même si ce n'est pas obligatoire, comme le print("\uac01")
montre). Cependant, dans de nombreuses circonstances pratiques, la longueur d'une chaîne correspond au nombre de caractères perçus par l'utilisateur, car de nombreux caractères sont généralement stockés par Python en tant que point de code Unicode unique.
En Python 2 , les chaînes Unicode sont appelées… "Chaînes Unicode" ( unicode
type, forme littérale u"…"
), tandis que les tableaux d'octets sont des "chaînes" ( str
type, où le tableau d'octets peut par exemple être construit avec des chaînes littérales "…"
). Dans Python 3 , les chaînes Unicode sont simplement appelées "chaînes" ( str
type, forme littérale "…"
), tandis que les tableaux d'octets sont des "octets" ( bytes
type, forme littérale b"…"
). En conséquence, quelque chose comme "🐍"[0]
donne un résultat différent dans Python 2 ( '\xf0'
, un octet) et Python 3 ( "🐍"
, le premier et le seul caractère).
Avec ces quelques points clés, vous devriez être capable de comprendre la plupart des questions liées à l'encodage!
Normalement, lorsque vous imprimez u"…"
sur un terminal , vous ne devriez pas avoir de déchets: Python connaît l'encodage de votre terminal. En fait, vous pouvez vérifier le codage attendu par le terminal:
% python
Python 2.7.6 (default, Nov 15 2013, 15:20:37)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Si vos caractères d'entrée peuvent être encodés avec l'encodage du terminal, Python le fera et enverra les octets correspondants à votre terminal sans se plaindre. Le terminal fera alors de son mieux pour afficher les caractères après avoir décodé les octets d'entrée (au pire, la police du terminal n'a pas certains des caractères et imprimera une sorte de blanc à la place).
Si vos caractères d'entrée ne peuvent pas être codés avec le codage du terminal, cela signifie que le terminal n'est pas configuré pour afficher ces caractères. Python se plaindra (en Python avec un UnicodeEncodeError
car la chaîne de caractères ne peut pas être encodée d'une manière qui convient à votre terminal). La seule solution possible est d'utiliser un terminal capable d'afficher les caractères (soit en configurant le terminal pour qu'il accepte un encodage pouvant représenter vos caractères, soit en utilisant un programme de terminal différent). Ceci est important lorsque vous distribuez des programmes qui peuvent être utilisés dans différents environnements: les messages que vous imprimez doivent être représentables dans le terminal de l'utilisateur. Parfois, il est donc préférable de s'en tenir aux chaînes qui ne contiennent que des caractères ASCII.
Cependant, lorsque vous redirigez ou redirigez la sortie de votre programme, il n'est généralement pas possible de savoir quel est le codage d'entrée du programme récepteur, et le code ci-dessus renvoie un codage par défaut: Aucun (Python 2.7) ou UTF-8 ( Python 3):
% python2.7 -c "import sys; print sys.stdout.encoding" | cat
None
% python3.4 -c "import sys; print(sys.stdout.encoding)" | cat
UTF-8
Le codage de stdin, stdout et stderr peut cependant être défini via la PYTHONIOENCODING
variable d'environnement, si nécessaire:
% PYTHONIOENCODING=UTF-8 python2.7 -c "import sys; print sys.stdout.encoding" | cat
UTF-8
Si l'impression sur un terminal ne produit pas ce que vous attendez, vous pouvez vérifier que le codage UTF-8 que vous avez mis manuellement est correct; par exemple, votre premier caractère ( \u001A
) n'est pas imprimable, si je ne me trompe pas .
Sur http://wiki.python.org/moin/PrintFails , vous pouvez trouver une solution comme la suivante, pour Python 2.x:
import codecs
import locale
import sys
# Wrap sys.stdout into a StreamWriter to allow writing unicode.
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni
Pour Python 3, vous pouvez vérifier l' une des questions posées précédemment sur StackOverflow.