Il peut être nécessaire de lire ou d'écrire des fichiers stockés sur l'ordinateur exécutant vos scripts. Consigner des données dans des fichiers permet de simplifier un programme en externalisant les données et peut être un moyen de s'interfacer avec d'autres programmes et systèmes ainsi qu'avec les utilisateurs. Nous utiliserons la fonction fournie par défaut open(). Avant tout, il est nécessaire de voir comment naviguer dans l'arborescence.
En fonction du répertoire dans lequel est exécuté votre script, il peut être nécessaire de changer de répertoire de travail du script. Pour se faire, nous utiliserons la fonction chdir(repertoire) dans le module os pour changer de répertoire de travail. Nous utiliserons également la fonction getcwd() du même module. Il est possible de créer un dossier avec mkdir(chemin) :
>>> from os import getcwd, chdir, mkdir >>> print(getcwd()) /home/antoine >>> chdir('essais') >>> print(getcwd()) /home/antoine/essais >>> mkdir('test')
Pour lire un fichier, il faut tout d'abord ouvrir un flux de lecture ou d'écriture de fichier avec la fonction open(fichier,mode (par défaut : 'rt')) avec fichier l'adresse du fichier à ouvrir et mode, le type de flux à ouvrir. Le mode est composé de deux lettres, les droits d'accès (rwxa) et l'encodage (bf). Voici le tableau détaillant les modes de flux de fichier.
Caractère | Action |
---|---|
'r' | Ouvrir en lecture seule (défaut) |
'w' | Ouvrir en écriture. Écrase le fichier existant. |
'x' | Ouvrir en écriture si et seulement si le fichier n'existe pas déjà. |
'a' | Ouvrir en écriture. Ajoute au fichier existant. |
'b' | Mode binaire. |
't' | Mode texte (défaut). |
Pour fermer le flux de fichier avec la méthode close() sur la variable représentant le flux.
Il est important de fermer le flux une fois les opérations sur le fichier terminé.
>>> fichier = open("texte.txt",'rt') >>> texte = fichier.read() >>> print(texte) Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque gravida erat ut lectus convallis auctor. Fusce mollis sem id tellus auctor hendrerit. >>> lignes = fichier.readlines() >>> print(lignes) ['Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n', 'Pellentesque gravida erat ut lectus convallis auctor.\n', 'Fusce mollis sem id tellus auctor hendrerit.\n'] >>> fichier.close()
Chaque ligne est terminée par "\n" qui représente un retour à la ligne. Il s'agit d'un caractère spécial. Si vous écrivez ce caractère dans une chaîne de caractères, Python produira un retour à la ligne :
>>> texte = "Première ligne\nDeuxième ligne" >>> print(texte) Première ligne Deuxième ligne
>>> fichier = open("texte.txt",'wt') >>> fichier.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit. \nPellentesque gravida erat ut lectus convallis auctor. \nFusce mollis sem id tellus auctor hendrerit.") >>> fichier.close()
>>> fichier = open("texte.txt",'wt') >>> fichier.writelines(["Lorem ipsum dolor sit amet, consectetur adipiscing elit. \n", "Pellentesque gravida erat ut lectus convallis auctor. \n", "Fusce mollis sem id tellus auctor hendrerit."]) >>> fichier.close()
Il existe différents formats standards de stockage de données. Il est recommandé de favoriser ces formats car il existe déjà des modules Python permettant de simplifier leur utilisation. De plus, ces formats sont adoptés par d'autres programmes avec lesquels vous serez peut-être amené à travailler.
Le fichier Comma-separated values (CSV) est un format permettant de stocker des tableaux dans un fichier texte. Chaque ligne est représentée par une ligne de texte et chaque colonne est séparée par un séparateur (virgule, point-virgule …).
Les champs texte peuvent également être délimités par des guillemets. Lorsqu'un champ contient lui-même des guillemets, ils sont doublés afin de ne pas être considérés comme début ou fin du champ. Si un champ contient un signe pouvant être utilisé comme séparateur de colonne (virgule, point-virgule …) ou comme séparateur de ligne, les guillemets sont donc obligatoires afin que ce signe ne soit pas confondu avec un séparateur.
Voici des données présentées sous la forme d'un tableau et d'un fichier CSV :
Nom;Prénom;Age "Dubois";"Marie";29 "Duval";"Julien ""Paul""";47 Jacquet;Bernard;51 Martin;"Lucie;Clara";14
Données sous la forme d'un fichier CSV
Nom | Prénom | Age |
---|---|---|
Dubois | Marie | 29 |
Duval | Julien "Paul" | 47 |
Jacquet | Bernard | 51 |
Martin | Lucie;Clara | 14 |
Données sous la forme d'un tableau
Le module csv de Python permet de simplifier l'utilisation des fichiers CSV.
Pour lire un fichier CSV, vous devez ouvrir un flux de lecture de fichier et ouvrir à partir de ce flux un lecteur CSV. Pour ignorer la ligne d'en-tête, utilisez next(lecteurCSV) :
import csv fichier = open("noms.csv", "rt") lecteurCSV = csv.reader(fichier,delimiter=";") # Ouverture du lecteur CSV en lui fournissant le caractère séparateur (ici ";") for ligne in lecteurCSV: print(ligne) # Exemple avec la 1e ligne du fichier d'exemple : ['Nom', 'Prénom', 'Age'] fichier.close()
Vous obtiendrez en résultat une liste contenant chaque colonne de la ligne en cours. Vous pouvez modifier le délimiteur de champs texte en définissant la variable quotechar :
import csv fichier = open("noms.csv", "rt") lecteurCSV = csv.reader(fichier,delimiter=";",quotechar="'") # Définit l'apostrophe comme délimiteur de champs texte for ligne in lecteurCSV: print(ligne) fichier.close()
Vous pouvez également lire les données et obtenir un dictionnaire par ligne contenant les données en utilisant DictReader au lieu de reader :
import csv fichier = open("noms.csv", "rt") lecteurCSV = csv.DictReader(fichier,delimiter=";") for ligne in lecteurCSV: print(ligne) # Résultat obtenu : {'Age': '29', 'Nom': 'Dubois', 'Prénom': 'Marie'} fichier.close()
import csv fichier = open("annuaire.csv", "wt") ecrivainCSV = csv.writer(fichier,delimiter=";") ecrivainCSV.writerow(["Nom","Prénom","Téléphone"]) # On écrit la ligne d'en-tête avec le titre des colonnes ecrivainCSV.writerow(["Dubois","Marie","0198546372"]) ecrivainCSV.writerow(["Duval","Julien \"Paul\"","0399741052"]) ecrivainCSV.writerow(["Jacquet","Bernard","0200749685"]) ecrivainCSV.writerow(["Martin","Julie;Clara","0399731590"]) fichier.close()
Nous obtenons le fichier suivant :
Nom;Prénom;Téléphone Dubois;Marie;0198546372 Duval;"Julien ""Paul""";0399741052 Jacquet;Bernard;0200749685 Martin;"Julie;Clara";0399731590
Il est également possible d'écrire le fichier en fournissant un dictionnaire par ligne à condition que chaque dictionnaire possède les mêmes clés. On doit également fournir la liste des clés des dictionnaires avec l'argument fieldnames :
import csv bonCommande = [ {"produit":"cahier", "reference":"F452CP", "quantite":41, "prixUnitaire":1.6}, {"produit":"stylo bleu", "reference":"D857BL", "quantite":18, "prixUnitaire":0.95}, {"produit":"stylo noir", "reference":"D857NO", "quantite":18, "prixUnitaire":0.95}, {"produit":"équerre", "reference":"GF955K", "quantite":4, "prixUnitaire":5.10}, {"produit":"compas", "reference":"RT42AX", "quantite":13, "prixUnitaire":5.25} ] fichier = open("bon-commande.csv", "wt") ecrivainCSV = csv.DictWriter(fichier,delimiter=";",fieldnames=bonCommande[0].keys()) ecrivainCSV.writeheader() # On écrit la ligne d'en-tête avec le titre des colonnes for ligne in bonCommande: ecrivainCSV.writerow(ligne) fichier.close()
Nous obtenons le fichier suivant :
reference;quantite;produit;prixUnitaire F452CP;41;cahier;1.6 D857BL;18;stylo bleu;0.95 D857NO;18;stylo noir;0.95 GF955K;4;équerre;5.1 RT42AX;13;compas;5.25
Par défaut, Python placera les guillemets autour des chaînes contenant des guillemets, une virgule ou un point virgule afin que ceux-ci ne soient pas confondus avec un délimiteur de champs ou le séparateur. Afin que tous les champs soient encadrés par les guillemets, nous allons modifier l'argument quoting pour writer ou DictWriter :
import csv fichier = open("annuaire.csv", "wt") ecrivainCSV = csv.writer(fichier,delimiter=";",quotechar="'", quoting=csv.QUOTE_ALL) # quotechar modifie le caractère délimitant un champ (par défaut : ") ecrivainCSV.writerow(["Nom","Prénom","Téléphone"]) # On écrit la ligne d'en-tête avec le titre des colonnes ecrivainCSV.writerow(["Dubois","Marie","0198546372"]) ecrivainCSV.writerow(["Duval","Julien \"Paul\"","0399741052"]) ecrivainCSV.writerow(["Jacquet","Bernard","0200749685"]) ecrivainCSV.writerow(["Martin","Julie;Clara","0399731590"]) fichier.close()
Nous obtenons le fichier suivant :
'Nom';'Prénom';'Téléphone' 'Dubois';'Marie';'0198546372' 'Duval';'Julien "Paul"';'0399741052' 'Jacquet';'Bernard';'0200749685' 'Martin';'Julie;Clara';'0399731590'
Le paramètre quoting peut prendre les valeurs suivantes.
Valeur | Action |
---|---|
csv.QUOTE_ALL | Met tous les champs entre guillemets. |
csv.QUOTE_MINIMAL | Met les guillemets autour des chaînes contenant des guillemets et le séparateur de champs (par défaut). |
csv.QUOTE_NONNUMERIC | Met les guillemets autour des valeurs non-numériques et indique au lecteur de convertir les valeurs non contenues entre les guillemets en nombres réels. |
csv.QUOTE_NONE | Ne met aucun guillemet. |
Le format JavaScript Object Notation (JSON) est issu de la notation des objets dans le langage JavaScript. Il s'agit aujourd'hui d'un format de données très répandu permettant de stocker des données sous une forme structurée.
Il ne comporte que des associations clés → valeurs (à l'instar des dictionnaires), ainsi que des listes ordonnées de valeurs (comme les listes en Python). Une valeur peut être une autre association clés → valeurs, une liste de valeurs, un entier, un nombre réel, une chaîne de caractères, un booléen ou une valeur nulle. Sa syntaxe est similaire à celle des dictionnaires Python.
Voici un exemple de fichier JSON :
{ "Dijon":{ "nomDepartement": "Côte d'Or", "codePostal": 21000, "population": { "2006": 151504, "2011": 151672, "2014": 153668 } }, "Troyes":{ "nomDepartement": "Aube", "codePostal": 10000, "population": { "2006": 61344, "2011": 60013, "2014": 60750 } } }
Il est également possible de compacter un fichier JSON en supprimant les tabulations et les retours à la ligne. On obtient ainsi :
{"Dijon":{"nomDepartement":"Côte d'Or","codePostal":21000,"population":{"2006":151504,"2011":151672,"2014":153668}},"Troyes":{"nomDepartement":"Aube","codePostal":10000,"population":{"2006":61344,"2011":60013,"2014":60750}}}
Pour lire et écrire des fichiers JSON, nous utiliserons le module json fourni nativement avec Python.
La fonction loads(texteJSON) permet de décoder le texte JSON passé en argument et de le transformer en dictionnaire ou une liste.
>>> import json >>> fichier = open("villes.json","rt") >>> villes = json.loads(fichier.read()) >>> print(villes) {'Troyes': {'population': {'2006': 61344, '2011': 60013, '2014': 60750}, 'codePostal': 10000, 'nomDepartement': 'Aube'}, 'Dijon': {'population': {'2006': 151504, '2011': 151672, '2014': 153668}, 'codePostal': 21000, 'nomDepartement': "Côte d'Or"}} >>> fichier.close()
On utilise la fonction dumps(variable, sort_keys=False) pour transformer un dictionnaire ou une liste en texte JSON en fournissant en argument la variable à transformer. La variable sort_keys permet de trier les clés dans l'ordre alphabétique.
import json quantiteFournitures = {"cahiers":134, "stylos":{"rouge":41,"bleu":74}, "gommes": 85} fichier = open("quantiteFournitures.json","wt") fichier.write(json.dumps(quantiteFournitures)) fichier.close()
Lors de l'écriture de vos premiers scripts, vous avez peut-être rencontré ce type de message d'erreurs :
>>> resultat = 42/0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero
Ce message signale l'erreur ainsi que la ligne à laquelle a été faite cette erreur. Or il est courant que les erreurs ne soient pas des erreurs lors de la programmation mais une mauvaise manipulation de la part de l'utilisateur. Par exemple, vous demandez à l'utilisateur de fournir un nombre et celui-ci vous fournit un texte ou que le fichier, que vous cherchez à lire, n'existe pas.
Il faut alors gérer ce type d'erreurs afin d'éviter une interruption involontaire de notre application. Pour cela, nous utiliserons les structures try.
Voici un exemple sur lequel nous allons ajouter un mécanisme de gestion d'erreurs :
age = input("Quel est votre age : ") age = int(age)
Voici l'erreur obtenue si la chaîne de caractères n'est pas un nombre :
>>> age = input("Quel est votre age : ") Quel est votre age : Alain >>> age = int(age) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: 'Alain'
La structure try se présente ainsi :
try: # La portion de code à tester except: # Que faire en cas d'erreur
Nous obtenons donc pour notre exemple :
>>> age = input("Quel est votre age : ") Quel est votre age : Alain >>> try: ... age = int(age) ... except: ... print("Erreur lors de la saisie. ") ... Erreur lors de la saisie.
Il est possible d'identifier l'erreur et d'effectuer une action en conséquence. Nous pouvons donc chaîner les instructions except en fournissant le type d'erreur. Le bloc else (optionnel) est exécuté s'il n'y a eu aucune erreur.
try: quantiteBoites = quantitePieces / nombresPiecesParBoites except TypeError: # Type incompatible avec l'opération print("Au moins une des variables n'est pas un nombre. ") except NameError: # Variable non définie print("Au moins une des variables n'est pas définie. ") except ZeroDivisionError: # Division par 0 print("Le nombres de pièces par boites est égal à 0. ") else: print("Il faut commander " + str(quantiteBoites) + " boîtes. ")
Le bloc finally (optionnel) est exécuté dans tous les cas (s'il y a eu des erreurs ou non).
Enfin, l'instruction pass ne fait rien : elle permet de laisser un bloc vide ce qui est utile pour les exceptions.
Python fournit deux modules permettant de gérer les fichiers et les répertoires. Le module os.path permet de lister des fichiers et des répertoires, d'effectuer des opérations sur les URI. Le module shutil permet de copier, déplacer, supprimer des éléments sur les systèmes de fichiers.
Dans ce chapitre, nous travaillerons sur des systèmes de fichiers Unix (GNU/Linux, MacOS …).
Nous allons étudier les fonctions permettant de manipuler les chemins de fichiers ou de répertoires du module os.path. La fonction basename(URI) renvoie le nom du fichier de l'adresse fourni en argument. À l'inverse, la fonction dirname(URI) renvoie le chemin jusqu'au fichier, sans le nom du fichier. Ces fonctions fonctionnent même si le fichier n'existe pas.
>>> import os.path >>> chemin = "/tmp/dir/dir2/monFichier.txt" >>> print(os.path.basename(chemin)) monFichier.txt >>> print(os.path.dirname(chemin)) /tmp/dir/dir2
La fonction exists(URI) renvoie True si le fichier ou le répertoire fournis en argument existent. Les fonctions isfile(URI) et isdir(URI) permettent respectivement de vérifier si le chemin mène à un fichier et un répertoire. Ces fonctions renvoient True si c'est le cas.
>>> import os.path >>> chemin = "/tmp/dir/dir2/monFichier.txt" >>> print(os.path.exists(chemin)) True >>> print(os.path.isfile(chemin)) True >>> print(os.path.isdir(chemin)) False >>> print(os.path.isdir(os.path.dirname(chemin))) True
La fonction listdir(repertoire) du module os.path retourne le contenu du répertoire passé en argument sans distinction entre les fichiers et les répertoires.
>>> import os.path >>> print(os.listdir("/tmp/dir")) ['villes.json', 'quantiteFournitures.json', 'dir2']
Il existe deux méthodes dans le module shutil permettant d'effectuer une copie. La fonction copy(source, destination) permet de copier un fichier, alors que copytree en fait de même avec les répertoires.
import shutil shutil.copytree("/tmp/dir/dir2","/tmp/dir/dir3") shutil.copy("/tmp/dir/dir2/monFichier.txt","/tmp/dir/exemple.txt")
La fonction move(source, destination) du module shutil permet de déplacer un fichier ou un répertoire. Cela peut également servir à renommer le fichier ou le répertoire.
import shutil shutil.move("/tmp/dir/dir3","/tmp/dir/perso")
La méthode remove(fichier) du module os et la fonction rmtree(dossier) du module shutil permettent respectivement de supprimer un fichier et un répertoire.
import os,shutil os.remove("/tmp/dir/exemple.txt") shutil.rmtree("/tmp/dir/perso")
Le module pickle permet de sérialiser des variables quelles qu'elles soient vers un fichier ouvert en flux binaire et de les restaurer. Cela équivaut à sauvegarder et restaurer l'état des variables.
La fonction dump(variable, fichier) permet d'exporter une variable vers un fichier et la fonction load(fichier) retourne la variable lue depuis le fichier.
L'ordre de sauvegarde et de restauration des variables doit être identique.
>>> import pickle >>> texte = "Écrit par Antoine de Saint-Exupéry" >>> quantiteFournitures = {"cahiers":134, "stylos":{"rouge":41,"bleu":74}, "gommes": 85} >>> fournitures = ["cahier", "crayon", "stylo", "trousse", "gomme"] >>> fichierSauvegarde = open("donnees","wb") >>> pickle.dump(texte, fichierSauvegarde) >>> pickle.dump(quantiteFournitures, fichierSauvegarde) >>> pickle.dump(fournitures, fichierSauvegarde) >>> fichierSauvegarde.close()
>>> import pickle >>> fichierSauvegarde = open("donnees","rb") >>> texte = pickle.load(fichierSauvegarde) >>> quantiteFournitures = pickle.load(fichierSauvegarde) >>> fournitures = pickle.load(fichierSauvegarde) >>> fichierSauvegarde.close() >>> print(texte) Écrit par Antoine de Saint-Exupéry >>> print(quantiteFournitures) {'stylos': {'bleu': 74, 'rouge': 41}, 'gommes': 85, 'cahiers': 134} >>> print(fournitures) ['cahier', 'crayon', 'stylo', 'trousse', 'gomme']
Écrivez un programme permettant à un utilisateur de deviner un mot choisi au hasard dans un fichier nommé mots.txt dans lequel chaque ligne comporte un mot en capitale. L'utilisateur a 7 chances pour découvrir le mot en proposant une lettre à chaque fois. Si la lettre proposée n'est pas dans le mot, une chance lui est retirée.
Exemple :
- - - - - - - - (7 chances) Entrez une lettre : S - - - - - - - - (6 chances) Entrez une lettre : O - O - - - - - - (6 chances) … Bravo ! Le mot était JOURNAUX. ou Perdu ! Le mot était JOURNAUX.
Vous pouvez télécharger un fichier de mots ici : Télécharger le fichier
Écrivez un programme permettant de chiffrer un fichier texte nommé texte.txt à l'aide du chiffrement par décalage dans lequel chaque lettre est remplacée par une autre à distance fixe choisie par l'utilisateur. Par exemple, si la distance choisie est de 4, un A est remplacé par un D, un B par un E, un C par un F … Le résultat sera écrit dans un fichier texte nommé chiffre.txt.
Texte en clair | ESOPE RESTE ICI ET SE REPOSE |
---|---|
Texte chiffré (distance 7) | LZVWL YLZAL PJP LA ZL YLWVZL |
Écrivez un programme permettant à partir d'un fichier texte en français d'en déduire la fréquence d'apparition des lettres qu'il contient. Le résultat de cette analyse sera consigné dans un fichier JSON. Pour des statistiques fiables, prenez un texte assez long. Vous pouvez utiliser une copie de
Le message suivant a été chiffré à l'aide de la technique de chiffrement par décalage et stocké dans le fichier chiffre.txt :
HFCMSG GS GWHIS ROBG ZS UFOBR SGH RS ZO TFOBQS OI QSBHFS RI RSDOFHSASBH RS Z OIPS RCBH SZZS SGH ZS QVST ZWSI SH O Z CISGH RS ZO FSUWCB UFOBR SGH ZO QCAAIBS G SHSBR ROBG ZO DZOWBS RS QVOADOUBS QFOMSIGS O DFCLWAWHS RI DOMG R CHVS SH RI DOMG R OFAOBQS QSHHS JWZZS RS DZOWBS OZZIJWOZS G SHOPZWH ROBG ZO JOZZSS RS ZO GSWBS
À l'aide des statistiques d'apparition des lettres issues de l'exercice précédent stockées dans le fichier statistiques.json, déduisez le message en clair du texte ci-dessus et stockez le dans le fichier clair.txt.
Écrivez un programme permettant de calculer la moyenne d'un étudiant dont les notes sont consignées dans un fichier CSV nommé notes.csv. Chaque colonne correspond à une matière. Vous devrez écrire un fichier JSON nommé moyennes.json consignant ces moyennes ainsi que la moyenne générale. Toutes les notes et les matières sont au cœfficient 1. Un exemple de fichier CSV est disponible ici : Télécharger le fichier
Exemple :
{ "Mathématiques": 8.411111111111111, "Sciences naturelles": 17.412, … "Moyenne générale": 14.965248762650994 }