Fichiers de valeurs séparées par des virgules

Objectif : Écrire et lire les fichiers au format csv (Comma Separated Value).
Version : 2.3 et suivantes

Le module csv est utile pour travailler sur des extractions de données de feuilles de calcul et de bases de données au format texte. Comme il n'y a pas de standard défini précisément, le module utilise des "dialectes" (dialect) pour lire/sauvegarder avec différents paramètres. Le module inclut un lecteur et un enregistreur générique ainsi qu'un dialecte pour travailler avec des fichiers créés ou destinés à Microsoft Excel.

Limitations

La version de csv de Python 2.5 ne supporte pas les données au format Unicode. Il y a également des "problèmes avec les caractère ASCII NUL". Il est recommandé d'utiliser de l'UTF-8 ou de l'ASCII imprimable. (NdT : ceci est corrigé dans les versions 3.0 et suivantes de Python où un fichier lu est automatiquement décodé en Unicode et où les chaînes de caractères sont nativement codés en Unicode.)

Lecture

La fonction reader() permet de créer un objet reader, nécessaire à la lecture de données depuis un fichier csv. L'objet reader peut par ailleurs être utilisé comme itérateur pour traiter les lignes du fichier les unes après les autres. Ainsi :

import csv
import sys
f = open(sys.argv[1], 'rt')
try:
    reader = csv.reader(f)
    for row in reader:
        print row
finally:
    f.close()

Le premier argument de reader() est la source des lignes de texte. N'importe quel objet itérable est accepté, ici un fichier est utilisé. On peut également passer par une instance de StringIO, une liste, etc. Les autres arguments optionnels sont utilisés pour contrôler la façon d'interpréter les données.

Le fichier "testdata.csv" est une exportation depuis NeoOffice.

$ python csv_reader.py testdata.csv
['Title 1', 'Title 2', 'Title 3']
['1', 'a', '08/18/07']
['2', 'b', '08/19/07']
['3', 'c', '08/20/07']
['4', 'd', '08/21/07']
['5', 'e', '08/22/07']
['6', 'f', '08/23/07']
['7', 'g', '08/24/07']
['8', 'h', '08/25/07']
['9', 'i', '08/26/07']

Pendant la lecture chaque ligne est convertie en liste de chaîne de caractères.

$ python csv_reader.py testdata.csv
['Title 1', 'Title 2', 'Title 3']
['1', 'a', '08/18/07']
['2', 'b', '08/19/07']
['3', 'c', '08/20/07']
['4', 'd', '08/21/07']
['5', 'e', '08/22/07']
['6', 'f', '08/23/07']
['7', 'g', '08/24/07']
['8', 'h', '08/25/07']
['9', 'i', '08/26/07']

Comme le module ne convertit pas automatiquement les données d'entrée, les colonnes contenant un type spécifique devront être converties manuellement. Les retours à la ligne à l'intérieur d'une chaîne d'un enregistrement sont gérés, c'est ce qui explique qu'une ligne du fichier de données ne corresponde pas toujours à un enregistrement.

"Title 1","Title 2","Title 3"
1,"first line
second line",08/18/07

$ python csv_reader.py testlinebreak.csv
['Title 1', 'Title 2', 'Title 3']
['1', 'first line\nsecond line', '08/18/07']

Écriture

L'export de données au format csv est aussi facile que la lecture. La fonction writer() crée un objet writer. Chaque ligne peut alors être écrite grâce à writerow().

import csv
import sys
f = open(sys.argv[1], 'wt')
try:
    writer = csv.writer(f)
    writer.writerow( ('Title 1', 'Title 2', 'Title 3') )
    for i in range(10):
        writer.writerow( (i+1, chr(ord('a') + i), '08/%02d/07' % (i+1)) )
finally:
    f.close()
print open(sys.argv[1], 'rt').read()

Le résultat ne ressemble pas exactement aux données utilisées pour l'exemple de lecture :

$ python csv_writer.py testout.csv
Title 1,Title 2,Title 3
1,a,08/01/07
2,b,08/02/07
3,c,08/03/07
4,d,08/04/07
5,e,08/05/07
6,f,08/06/07
7,g,08/07/07
8,h,08/08/07
9,i,08/09/07
10,j,08/10/07

En effet, les guillemets sont ajoutés de manière différente par le writer : les colonnes de chaînes de caractères ne sont pas mises entre guillemets. Pour ajouter des guillemets autour des valeurs non-numériques il suffit de définir l'option quoting à la bonne valeur :

writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC)

Les chaînes de caractères sont maintenant mises entre guillemets :

$ python csv_writer_quoted.py testout_quoted.csv
"Title 1","Title 2","Title 3"
1,"a","08/01/07"
2,"b","08/02/07"
3,"c","08/03/07"
4,"d","08/04/07"
5,"e","08/05/07"
6,"f","08/06/07"
7,"g","08/07/07"
8,"h","08/08/07"
9,"i","08/09/07"
10,"j","08/10/07"

Guillemets

Il y a quatre options différentes, définies en tant que constantes du module csv, pour la mise entre guillemets.

QUOTE_ALL
Met des guillemets partout, indépendamment du type.
QUOTE_MINIMAL
Ajoute des guillemets uniquement en cas de caractères spéciaux (définis comme étant les caractères qui pourraient perturber un lecteur munit des mêmes options). Il s'agit de la valeur de quoting par défaut.
QUOTE_NONNUMERIC
Met des guillemets sur tout les champs ne contenant ni des entiers ni des nombres à virgules. Quand elle utilisée cette option fait convertir par le lecteur tous les champs non entourés de guillemets en nombres à virgules (float).
QUOTE_NONE
Ne met pas de guillemets du tout. Utiliser cette option permet au lecteur d'inclure les guillemets dans les chaînes lues (normalement ils sont traités comme des délimiteurs et enlevés).

Dialectes

Il y a beaucoup de paramètres qui contrôlent la façon dont le module lit et écrit les données. Plutôt que de les passer individuellement aux fonctions writer() et reader(), ils sont rassemblés en objets dialect. Les objets dialect peuvent être passés par leur nom, ainsi les paramètres n'ont pas besoin d'être connus à l'avance lors de l'appel du module csv. Le module inclut en standard 2 objets dialect : excel et excel-tabs. L'objet dialect excel permet de travailler avec des données exportées au format par défaut depuis Microsoft Excel. Ce dialecte permet également de travailler avec des données issues d'OpenOffice ou de NeoOffice.

Supposons que le fichier utilise des caractères pipe "|" plutôt que des points-virgules :

"Title 1"|"Title 2"|"Title 3"
1|"first line
second line"|08/18/07

Un nouvel objet dialect peut alors être créé avec les paramètres appropriés :

import csv
csv.register_dialect('pipes', delimiter='|')
with open('testdata.pipes', 'r') as f:
    reader = csv.reader(f, dialect='pipes')
    for row in reader:
        print row

Le fichier peut ensuite être lu comme un fichier délimité par des points-virgules :

$ python csv_dialect.py
['Title 1', 'Title 2', 'Title 3']
['1', 'first line\nsecond line', '08/18/07']

Pour plus de détails sur les paramètres des objets dialect et leur utilisation peuvent être trouvés dans la documentation pour le module csv de la librairie standard.

DictReader et DictWriter

En plus de permettre de travailler avec des lignes des données sous forme de listes, le module csv permet de traiter celles-ci comme des dictionnaires. Les clés de ce dictionnaire peuvent être fournies en paramètres ou déduites de la première colonne (utile par exemple quand les enregistrements ont un intitulé).

import csv
import sys
f = open(sys.argv[1], 'rt')
try:
    reader = csv.DictReader(f)
    for row in reader:
        print row
finally:
    f.close()

Les fonctions reader et writer basées sur les dictionnaires sont implémentés autour des fonctions similaires pour les listes, et utilise les même arguments et les mêmes API. La différence réside dans le fait que les enregistrements sont des dictionnaires plutôt que des listes ou des tuples.

$ python csv_dictreader.py testdata.csv
{'Title 1': '1', 'Title 3': '08/18/07', 'Title 2': 'a'}
{'Title 1': '2', 'Title 3': '08/19/07', 'Title 2': 'b'}
{'Title 1': '3', 'Title 3': '08/20/07', 'Title 2': 'c'}
{'Title 1': '4', 'Title 3': '08/21/07', 'Title 2': 'd'}
{'Title 1': '5', 'Title 3': '08/22/07', 'Title 2': 'e'}
{'Title 1': '6', 'Title 3': '08/23/07', 'Title 2': 'f'}
{'Title 1': '7', 'Title 3': '08/24/07', 'Title 2': 'g'}
{'Title 1': '8', 'Title 3': '08/25/07', 'Title 2': 'h'}
{'Title 1': '9', 'Title 3': '08/26/07', 'Title 2': 'i'}

DictWriter reçoit comme argument une liste d'intitulé, ce qui permet de savoir comment ordonner la sortie.

import csv
import sys
f = open(sys.argv[1], 'wt')
try:
    fieldnames = ('Title 1', 'Title 2', 'Title 3')
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    headers = dict( (n,n) for n in fieldnames )
    writer.writerow(headers)
    for i in range(10):
        writer.writerow({ 'Title 1'  :i+1,
                          'Title 2' : chr(ord('a') + i),
                          'Title 3' : '08/%02d/07' % (i+1),
                          })
finally:
    f.close()
print open(sys.argv[1], 'rt').read()
$ python csv_dictwriter.py testout.csv
Title 1,Title 2,Title 3
1,a,08/01/07
2,b,08/02/07
3,c,08/03/07
4,d,08/04/07
5,e,08/05/07
6,f,08/06/07
7,g,08/07/07
8,h,08/08/07
9,i,08/09/07
10,j,08/10/07

Voir aussi :

csv

   La documentation standard pour ce module.

PEP 305

   La PEP qui décrit le module et son API.

csv – Comma-separated value files

   La version originale de ce PyMOTW.