Ayant récemment eu à stocker et récupérer de
l'
Au moment où on crée une nouvelle base, on peut spécifier
l'encodage de celle-ci. Je recommande toujours l'encodage UNICODE (qui
est en fait de l'
Pour manipuler des caractères, il vaut mieux suivre deux règles
simples :
Un avertissement préalable (merci à Daniel Verite pour ses
explications) : il vaut mieux que la structure des bases de données
aie été créée avec le même encodage que les bases. Comme le dit la
documentation de PostgreSQL,
postgres=# create database u8 encoding 'UTF8';
CREATE DATABASE
postgres=# \c u8
...
postgres=# set client_encoding=latin1;
SET
u8=# select lower('à')
ERROR: invalid byte sequence for encoding "UTF8": 0xe3a0
HINT: This error can also happen if the byte sequence does not match the encoding expected by the server, which is controlled by "client_encoding".
c'est probablement à cause de cette incohérence.
Pour l'éviter, je suggère que
% /usr/lib/postgresql/$PG_VERSION/bin/initdb --encoding=UTF8 --locale=fr_FR.UTF8 --pgdata=/var/lib/postgresql/$PG_VERSION/main
et s'assurer que le serveur a bien une locale UTF-8. Par exemple,
dans le paquetage
LC_CTYPE=fr_FR.UTF8
dans
Une fois passé ce piège, je crée la base (si on a suivi les
conseils précédents, l'option
% createdb --encoding UNICODE ${DB}
% psql -f ./create.sql ${DB}
où le fichier
CREATE TABLE Adresses (
id SERIAL UNIQUE NOT NULL,
prenom TEXT,
nom TEXT
);
Je peux ensuite remplir la base. D'abord, utilisons
% psql -f ./insert-utf8.sql ${DB}
où
INSERT INTO Adresses (Prenom, Nom)
VALUES ('Stéphane', 'Bortzmeyer');
INSERT INTO Adresses (Prenom, Nom)
VALUES ('Pierre', 'Louÿs');
Si on préfère que le fichier soit en Latin-1, parce qu'on édite plus
facilement du Latin-1, rien de plus simple, mais il faut l'indiquer à
PostgreSQL :
SET CLIENT_ENCODING='iso-8859-1';
INSERT INTO Adresses (Prenom, Nom)
VALUES ('Stéphane', 'Bortzmeyer');
INSERT INTO Adresses (Prenom, Nom)
VALUES ('Pierre', 'Louÿs');
et
% psql -f ./insert-latin1.sql ${DB}
produira le résultat attendu.
L'interêt de gérer des fichiers UTF-8 est que cela permet de
représenter des caractères qui n'existent pas dans Latin-1. Si vous
n'avez affaire qu'à des noms français, ce n'est pas un problème mais
supposons que vous vouliez enregistrer le nom d'une
INSERT INTO Adresses (Prenom, Nom)
VALUES ('Marii', 'Curie-Skłodowskiej');
et ces caractères coexisteront sans problèmes avec ceux utilisés en
français. C'est bien pour cela qu'Unicode a été inventé.
Le support d'Unicode dans PostgreSQL n'est pas sans faille. La
recherche avec les expressions
rationnelles, par exemple, ne fonctionne que partiellement :
carnet=> SELECT * FROM adresses WHERE prenom ~ 'St.phane';
id | prenom | nom
----+----------+------------
19 | Stéphane | Bortzmeyer
(2 rows)
carnet=> SELECT * FROM adresses WHERE prenom ~ 'St\wphane';
id | prenom | nom
----+--------+-----
(0 rows)
Le point (qui remplace n'importe quel caractère) a bien fonctionné
(PostgreSQL comprend bien la différence entre un
De même, je n'ai pas pu faire fonctionner la capitalisation pour l'opérateur ILIKE (comme LIKE, mais
insensible à la
carnet=> SELECT * FROM adresses WHERE prenom ILIKE '%stéphane%';
id | prenom | nom
----+----------+------------
5 | Stéphane | Bortzmeyer
(1 rows)
carnet=> SELECT * FROM adresses WHERE prenom ILIKE '%STÉPHANE%';
id | prenom | nom
----+--------+-----
(0 rows)
C'est le genre de petits problèmes agaçants qu'on rencontre souvent avec Unicode.
Affichons maintenant ce qu'on a enregistré.
PGCLIENTENCODING=iso-8859-1 psql -c "SELECT * FROM Adresses" ${DB}
Et on voit bien les caractères, qu'ils aient été entrée en UTF-8 ou
bien en Latin-1 :
id | prenom | nom
----+----------+------------
5 | Stéphane | Bortzmeyer
6 | Pierre | Louÿs
7 | Stéphane | Bortzmeyer
8 | Pierre | Louÿs
(4 rows)
(Naturellement, les noms qui ne sont pas représentables en Latin-1
seront malmenés.)
Si maintenant, on veut traiter ces données dans un programme, c'est
également possible, ici en
#!/usr/bin/python
import psycopg
import sys
db_encoding = "UTF-8"
if len(sys.argv) <= 1:
sys.stderr.write("Usage: %s database [encoding]\n" % sys.argv[0])
sys.exit(1)
db = sys.argv[1]
if len(sys.argv) > 2:
def tr(text):
return text.decode(db_encoding).encode(sys.argv[2], 'replace')
else:
def tr(text):
return text
connection = psycopg.connect("dbname=%s" % db)
cursor = connection.cursor()
cursor.execute("""
SELECT prenom,nom FROM Adresses
""")
for mytuple in cursor.fetchall():
sys.stdout.write("%s %s\n" % (tr(mytuple[0]), tr(mytuple[1])))
cursor.close()
connection.close()
Ce programme va lire les données de la base, il peut les convertir en
Unicode (Python manipule nativement l'Unicode), pour traitement
(
Si on souhaite publier sur le
Tous ces programmes ont été testés avec PostgreSQL 7.4 et 8.1.