PYTHON facile

Les chaînes unicodes et Python.

 

Python et Unicode

Ou comment utiliser les chaînes unicode avec Python ?

Ces explications sont données pour mon environnement de développement : Windows 98 pour le système d'exploitation et Scite mon éditeur de modules, mais elle peuvent sûrement être valables pour d'autres environnements.

Bien comprendre son environnement de développement, c'est trouver tous les endroits ou peuvent se trouver des chaînes unicode ou des encodages différents. Par la suite je parlerai d'encode ou encodage pour la transformation d'une chaîne normale en chaîne unicode et décode ou décodage pour l'inverse.


Les différents codages

Le codage de base

Sans instructions particulières, Python encode toutes les chaînes en considérant quelles sont écrites avec le codage 'ascii'. Je lance Python avec l'option -S pour ne pas importer ni le module site ni le module sitecustomize.

C:\WINDOWS>python -S
Python 2.3.2 (#49, Oct  2 2003, 20:02:00) [ ... ] on win32
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
>>>

L'encodage par défaut de l'utilisateur

Chaque utilisateur utilise suivant ses préférences, une 'locale' qui définit en autre la langue et l'encodage par défaut de son environnement de travail, ici le français 'fr_Fr' et le codage 'cp1252'. C'est à dire le système considère que les chaînes sont dans ce format.

C:\WINDOWS>python
Python 2.3.2 (#49, Oct  2 2003, 20:02:00) [ ... ] on win32
Type "help", "copyright", "credits" or "license" for ...
>>> import locale
>>> locale.getdefaultlocale()
('fr_FR', 'cp1252')
>>>

Les données d'entrée

La fenêtre DOS

Le test : création d'une chaîne unicode à partir d'une entrée clavier. La chaînes utilisée est '¤€éè'

Dans les exemples de codes si dessous le caractère 'Ç' représente le caractère '€' car l'euro n'existe pas dans l'encodage 'cp850', le jeu de caractères utilisé par les fenêtres DOS.

Premier cas, si on ne spécifie pas d'encodage, on retrouve le problème classique de l'encodage par défaut 'ascii'.

>>> a = unicode('¤Çéè')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeDecodeError: 'ascii' codec can't decode byte 0xcf in
position 0: ordinal not in range(128)

Cas suivant, on précise un mauvais encodage 'cp1252' ('latin1' ou 'iso-8859-15') cela oblige a re-encoder les données avec le même mauvais encodage.

>>> a = unicode('¤Çéè', 'cp1252')
>>> a
u'\xcf\u20ac\u201a\u0160'
>>> print a
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "C:\PYTHON23\lib\encodings\cp850.py", line 18, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode characters in
position 1-3: character maps to <undefined>
>>> print a.encode('cp850')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "C:\PYTHON23\lib\encodings\cp850.py", line 18, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode characters in
position 1-3: character maps to <undefined>
>>> print a.encode('cp1252')
¤Çéè

Dernier cas, on donne le bon encodage 'cp850' et la chaîne unicode est utilisable directement.

>>> a = unicode('¤Çéè', 'cp850')
>>> a
u'\xa4\xc7\xe9\xe8'
>>> print a
¤Çéè

J'ai découvert récemment une autre méthode moins empirique pour déterminer le code utilisé par le terminal de saisie. Un objet fichier posséde un attribut encoding. Les pseudos-fichers stdout et stdin du module sys peuvent ainsi nous renseigner.

IDLE

Si on refais le test dans l'interpréteur de commande de 'IDLE', on trouve pour le codage 'cp1252'.

>>> a = unicode('¤€éè','cp1252')
>>> a
u'\xa4\u20ac\xe9\xe8'
>>> print a
¤€éè

Formats des données

Le format de sauvegarde du module

Autre source de codage, le format de sauvegarde du module. Avec 'Scite', on peux sauvegarder son fichier au format '8-bit' (cp1252) ou utf-8. Ce qui fait que chaque caractère sera écrit de façon différente dans le fichier. Ainsi le caractère '€' sera codé sur un octet '\x80' pour cp1252 et deux octets '\u20ac' avec utf8.

Le fichier doit comporter une entête précisant son codage, sur la 2ème ligne, lorsque le fichier n'est pas codé en 'utf8' :

#!/usr/bin/env python
# -*- coding: cp1252 -*-

Voir cette page de la documentation pour plus de renseignement.

Avec 'Scite' si on sauvegarde en '8-bit', il est préférable d'utiliser 'cp1252' sauf si votre code ne comporte pas de caractères spécifique à ce codage comme '€', alors on pourra choisir 'latin1'.

Cette page présente les pages de codes 'cp1252' et 'latin1' avec leurs différences.

Ensuite, il faut utiliser le bon format pour les chaînes unicodes. Par exemple avec la fonction unicode(data, codec), codec (par défaut 'ascii') est à remplacer par 'cp1252' ou 'utf8'.

Le format des données externes

Il est parfois difficile de trouver le bon codage pour le fichier que l'on doit traiter avec python. Il n'existe aucune méthode universelle pour déterminer le codage d'un fichier.

Certains fichiers annoncent leur codage par une ligne spécifique. Par exemple, les fichiers 'html' peuvent avoir la balise suivante :
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />.

On peux aussi trouver un module permettant de trouver le codage d'un fichier xml.

Mais le plus simple est de savoir quel est le codage du fichier que l'on doit manipuler.


Terminal de sortie

La fenêtre DOS

Les modules python étant écris dans un code ('cp1252', 'latin1' ou 'utf8') différent de celui de la fenêtre DOS ('cp850'), il est obligatoire d'utiliser la méthode encode(codec, mode) du type chaîne avec comme valeur 'cp850' pour codec.

J'ai développé une classe PrintIso pour automatiser et simplifier tout cela.

Bug et fenêtre DOS

Il existe un bug lors de l'utilisation des chaînes unicodes utilisant directement la norme unicode : u'\u20ac' (caractère €), il faut encoder la sortie vers la fenêtre DOS avec l'encodage 'cp1252'.

>>> a = u'\u20ac'
>>> a
u'\u20ac'
>>> print a
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "C:\PYTHON23\lib\encodings\cp850.py", line 18, in encode
    return codecs.charmap_encode(input,errors,encoding_map)
UnicodeEncodeError: 'charmap' codec can't encode character
u'\u20ac' in position 0: character maps to <undefined>
>>> print a.encode('cp1252')
Ç

Création d'un module sitecustomize.py

Le module sitecustomize permet en autres de définir le codage par défaut de la plate-forme.

La lancement de Python passe par la procédure suivante :

  1. Le module site utilise la fonction sys.setdefaultencoding() pour lier la valeur de l'encodage par défaut de Python : 'ascii'.

  2. Ensuite il importe le module sitecustomize si il existe.

  3. Dans ce module, on peut définir, en autre, son encodage qui deviendra l'encodage par défaut.

  4. Puis il supprime la fonction setdefaultencoding du module sys ce qui empêche une nouvelle liaison.

Voici un exemple de module sitecustomize qui est tres largement inspiré du module site.

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

# Recuperation de la 'locale' par default.
import locale, sys
loc = locale.getdefaultlocale()
if loc[1]:
    encoding = loc[1]

# une version Non-Unicode retournera AttributeError...
if encoding:
    sys.setdefaultencoding(encoding)

En conclusion

L'utilisation des chaînes unicodes dépends de la bonne connaissance de son environnement et du bon usage des encodages/décodages.


Contact

Pour tous renseignements et critiques: E-mail

Valid XHTML 1.0! Valid CSS 2.0!