Gestion des fichiers¶
Python permet très facilement de manipuler des fichiers tout en garantissant une très bonne portabilité du code quel que soit le système d’exploitation sur lequel s’exécute le programme (pour peu que le développeur soit attentif à ce type de problématique).
Les opérations de manipulation de fichier : écriture, lecture, création, suppression
sont toutes susceptibles d’échouer. Dans ce cas, les fonctions ou les méthodes
produiront une erreur de type OSError
ou d’un type héritant de cette
exception.
Ouvrir et fermer un fichier¶
La fonction open()
permet de récupérer un descripteur de fichier en
passant en paramètre le chemin de ce fichier. Le descripteur de fichier va
nous permettre de réaliser les opérations de lecture et/ou d’écriture dans le
fichier.
Un descripteur de fichier est une ressource système. Cela signifie qu’il est
géré par le système d’exploitation. Lorsque les opérations sur le fichier sont
terminées, il faut fermer le descripteur de fichier à l’aide de la méthode
close()
. Ne pas fermer un descripteur de fichier conduit à une fuite
de ressources (resource leak). Si un programme ouvre trop de fichiers sans
jamais fermer les descripteurs de fichiers, le système d’exploitation peut
finir par refuser d’ouvrir des fichiers supplémentaires.
f = open("monfichier.txt)
# faire des opérations sur le contenu du fichier
f.close()
Comme des erreurs peuvent survenir lors de la lecture ou de l’écriture dans
un fichier ou comme il est très facile d’oublier d’appeler la méthode
close()
, Python fournit une syntaxe spéciale :
le with
.
with open("monfichier.txt") as f:
# faire des opérations sur le contenu du fichier
pass
La syntaxe with
appelle automatiquement la méthode
close()
du descripteur de fichier à la sortie du bloc (même si
la sortie du bloc est due à une erreur).
Note
La syntaxe with
fonctionne en Python pour divers types
de ressources du système : les fichiers, les accès réseau, les processus,
les objets de synchronisation entre processus et threads.
Vous pouvez gérer plusieurs ressources avec un with
:
with open("monfichier.txt") as f, open("autrefichier.txt") as f2:
# faire des opérations sur le contenu des fichiers
pass
Si vous souhaitez développer une classe qui fonctionne comme un gestionnaire
de ressources et dont les instances peuvent être initialisées dans une structure
with
comme les descripteurs de fichiers, alors votre classe
doit fournir une implémentation pour les méthodes spéciales
__enter__()
et __exit__()
.
Lire le contenu d’un fichier¶
Par défaut, la fonction open()
permet de lire le contenu d’un
fichier texte.
Supposons que nous ayons un fichier nommé dialogues.txt
dans le répertoire
courant.
- Halt! Who goes there?
- It is I, Arthur, son of Uther Pendragon, from the castle
of Camelot. King of the Britons, defeator of the Saxons,
sovereign of all England!
Pour obtenir le contenu du fichier dans une chaîne de caractères :
with open("dialogues.txt") as f:
contenu = f.read()
print(contenu)
Pour obtenir le contenu du fichier sous la forme d’un tableau de chaînes de caractères (un élément par ligne) :
with open("dialogues.txt") as f:
lignes = f.readlines()
print(len(lignes))
# affiche 5
Un descripteur de fichier agit également comme une séquence sur les lignes d’un fichier.
with open("dialogues.txt") as f:
for ligne in f:
print(ligne)
Note
Lorsqu’on lit un fichier texte, chaque ligne inclue le caractère de retour
à la ligne présent dans le fichier. Pour le supprimer, on peut utiliser
la méthode str.rstrip()
with open("dialogues.txt") as f:
for ligne in f:
print(ligne.rstrip('\n'))
Les modes de fichier¶
Lorsqu’on ouvre un fichier, il faut préciser le mode d’ouverture qui dépend
du type du fichier et des opérations que l’on souhaite réaliser. Le mode est
représenté par une chaîne de caractères qui est passée à la fonction open()
avec le paramètre mode
.
Mode |
Description |
---|---|
r |
ouverture en lecture (mode par défaut) |
w |
ouverture en écriture (efface le contenu précédent) |
x |
ouverture uniquement pour création (l’ouverture échoue si le fichier existe déjà) |
+ |
ouverture en lecture et écriture |
a |
ouverture en écriture pour ajout en fin de fichier |
b |
fichier binaire |
t |
fichier texte (mode par défaut) |
# ouverture en écriture
with open("dialogues.txt", mode="w") as f:
pass
# ouverture d'un fichier binaire en création
with open("fichier.bin", mode="xb") as f:
pass
Spécificité du mode texte¶
Ouvrir un fichier en mode texte (mode par défaut ou t
) entraîne
un travail de conversion par Python. Convertir les données d’un fichier
en chaîne de caractères exige d’utiliser une famille d’encodage. Il est
possible de préciser la famille d’encodage d’un fichier grâce au paramètre
encoding
:
# ouverture en écriture
with open("dialogues.txt", encoding="utf-8") as f:
pass
Si le paramètre encoding
n’est pas spécifié alors Python utilise
un encodage qui est dépendant du système qui exécute le code (ce qui
peut nuire à la portabilité des fichiers produits). Pour connaître l’encodage
utilisé par défaut par l’interpréteur, il faut utiliser les méthodes du
module locale
:
>>> import locale
>>> locale.getpreferredencoding()
'UTF-8'
Le mode texte entraîne également une conversion des caractères de
fin de ligne
puisque tous les systèmes d’exploitation n’utilisent pas la même convention.
Python garantit une représentation universelle du caractère de fin de ligne
en utilisant \n
.
Le mode binaire (b
) est un mode qui permet d’accéder directement au
contenu du fichier sans conversion de la part de Python. Dans ce cas,
la lecture du fichier retourne des bytes
et non pas des chaînes
de caractères.
Écrire dans un fichier¶
Pour écrire d’un bloc dans un fichier, on peut utiliser la méthode
write()
et pour écrire une liste de lignes, il faut utiliser
la méthode writelines()
. Attention pour les fichiers textes,
ces méthodes n’ajoutent pas de caractères de fin de ligne, il faut donc les
écrire explicitement.
lignes = ["- Pull the other one!\n",
"- I am. And this my trusty servant Patsy.\n"]
# ouverture d'un fichier texte en ajout
with open("dialogues.txt", mode="a") as f:
f.writelines(lignes)
Prudence
Il faut se rappeler que le mode d’ouverture en écriture (w
) remplace
intégralement le contenu du fichier. Pour ajouter à la fin du fichier, il
faut ouvrir le fichier en mode ajout (a
) sous peine de perdre tout
le contenu précédemment sauvé.
Chemin de fichier¶
Les fichiers sont organisés selon une structure arborescente dans laquelle
un nœud peut être soit un fichier soit un répertoire. Même si tous les systèmes
d’exploitation suivent le même principe, il existe des différences majeures
d’organisation. Le système MS-Windows utilise un système de fichiers multi-têtes
(c:, d:, e:…) tandis que les systèmes *nix et MacOS utilisent un système
mono-tête dont la racine est /
. Dans la représentation des chemins
de fichiers, MS-Windows utilise le caractère \
pour séparer les composants
d’un chemin :
C:\\Users\david\Documents\monfichier.txt
tandis que les systèmes *nix et MacOs utilisent le caractère /
/home/david/Documents/monfichier.txt
Enfin, il faut se souvenir que le système de fichiers de MS-Windows n’est pas sensible à la casse (case insensitive), c’est-à-dire que les mots peuvent être écrits en lettres majuscules ou en lettres minuscules. Au contraire, les systèmes *nix et MacOS sont sensibles à la casse (case sensitive), c’est-à-dire qu’un mot écrit en lettres majuscules est différent d’un mot écrit en lettres minuscules.
Toutes ces nuances peuvent rendre difficiles l’écriture d’un programme portable d’un système à l’autre. Heureusement, la bibliothèque standard Python fournit plusieurs solutions pour aider les développeurs.
Le module os.path¶
Le module os.path
fournit des fonctions élémentaires pour nous aider à
gérer les chemins de fichiers.
join()
Cette fonction permet de créer un chemin en utilisant le séparateur approprié pour le système.
import os.path as path chemin = path.join("fichiers", "monfichier.txt") print(chemin) # Sous Windows affiche fichiers\monfichier.txt # Sous *nix ou MacOS, affiche fichiers/monfichier.txt
abspath()
Cette fonction retourne le chemin absolu.
import os.path as path chemin = path.abspath("monfichier.txt") print(chemin) # Si le répertoire de travail est /home/david/Documents # affiche /home/david/Documents/monfichier.txt
Le module pathlib¶
Le module pathlib
est un module de haut-niveau qui permet à la fois
de manipuler un chemin mais également d’interagir avec le fichier ou le répertoire
désigné par ce chemin. C’est un module tout-en-un qui facilite grandement le
travail sur les fichiers. L’élément central du module est la classe
Path
.
>>> from pathlib import Path
>>> path = Path("/", "home", "david", "Documents", "monfichier.txt")
>>> str(path)
'/home/david/Documents/monfichier.txt'
>>> path.parts
('/', 'home', 'david', 'Documents', 'monfichier.txt')
>>> path.root
'/'
>>> path.drive
''
>>> path.name
'monfichier.txt'
>>> parent = path.parent
>>> parent.parts
('/', 'home', 'david', 'Documents')
>>> path.is_file()
True
>>> path.is_dir()
False
>>> path.parent.is_file()
False
>>> path.parent.is_dir()
True
La classe Path
possède la méthode open()
qui accepte les mêmes paramètres que la fonction open()
sauf le chemin
qui est déjà représenté par l’objet lui-même :
from pathlib import Path
chemin = Path("dialogues.txt")
with chemin.open() as f:
for ligne in f:
print(ligne)
On peut même faire l’économie de ce code en appelant la méthode read_text()
qui ouvre le fichier en mode texte, lit l’intégralité du fichier et referme le fichier :
from pathlib import Path
chemin = Path("dialogues.txt")
contenu = chemin.read_text()
Pour construire un chemin à partir d’un autre chemin, il suffit
d’utiliser l’opérateur /
qui est utilisé, non pas comme opérateur de la
division, mais comme le séparateur universel de chemin de fichiers :
>>> chemin = Path("mondossier")
>>> chemin_fichier = chemin / "mon fichier.txt"
>>> chemin_fichier.parts
('mondossier', 'mon fichier.txt')
>>> chemin_fichier = Path.home() / "Documents" / "monfichier.txt"
>>> str(chemin_fichier)
'/home/david/Documents/monfichier.txt'
Actions sur les fichiers et les répertoires¶
Il est possible de réaliser des opérations élémentaires sur les fichiers et
les répertoires soit avec les modules os
et shutil
soit avec le module pathlib
.
Les modules os
et shutil
sont historiquement les premiers modules qui ont été introduits
en Python. Ils proposent surtout des fonctions alors que le module pathlib
est orienté objet avec notamment la classe Path
.
Connaître le répertoire de travail¶
Le répertoire de travail correspond au répertoire courant au moment du lancement de l’interpréteur Python.
Connaître le répertoire de l’utilisateur¶
Copier un fichier¶
Supprimer un fichier¶
Créer un répertoire¶
Supprimer un répertoire¶
Pour supprimer un répertoire, ce dernier doit être vide.
Vérifier qu’un fichier existe¶
Lister le contenu d’un répertoire¶
>>> import pathlib
>>> p = pathlib.Path("monrepertoire")
>>> liste_fichiers = list(p.iterdir())
La méthode iterdir()
retourne un itérateur sur des objets de
type Path
plutôt qu’un tableau de chaînes de caractères comme
os.listdir()
.
Rechercher des fichiers¶
Le module glob
permet d’effectuer une recherche dans l’arborescence
de fichiers. On peut utiliser le caractère ?
pour représenter n’importe
quel caractère et *
pour représenter n’importe quelle suite de caractères.
Il est possible d’effectuer une recherche récursive (c’est-à-dire en incluant les sous répertoires)
en positionnant la paramètre recursive
à True
et en utilisant
la séquence **
pour indiquer un ou plusieurs sous répertoires.
La classe Path
possède également la méthode
glob()
.
>>> import pathlib
>>> liste_fichiers = list(pathlib.Path.cwd().glob("**/*.py"))
La méthode glob()
retourne un itérateur sur des objets de
type Path
plutôt qu’un tableau de chaînes de caractères comme
glob.glob()
.
Lecture de fichiers CSV¶
Le fichier CSV (comma separated values) est un format texte très simple pour
stocker des tables de données. Le module csv
offre des méthodes pour lire
et écrire.
Si on dispose du fichier suivant :
1971,And Now for Something Completely Different
1975,Holy Grail
1979,Life of Brian
1983,The Meaning of Life
1996,The Wind in the Willows
import csv
with open("filmographie.txt") as f:
lecteur = csv.reader(f)
for ligne in lecteur:
print(ligne)
Chaque ligne lue est un tableau de chaînes de caractères contenant chaque valeur en colonne :
['1971', 'And Now for Something Completely Different']
['1975', 'Holy Grail']
['1979', 'Life of Brian']
['1983', 'The Meaning of Life']
['1996', 'The Wind in the Willows']
Exercices¶
Exercice : modifier un fichier texte
Écrivez un programme qui ouvre un fichier texte pour afficher son contenu.
Le programme modifie les lignes affichées afin qu’elles commencent par une
majuscule (vous pouvez utiliser la méthode capitalize()
).
Le chemin du fichier peut être demandé à l’utilisateur ou passé en paramètre du script.
Vous pouvez utiliser le fichier chanson.txt
:
some things in life are bad
they can really make you mad
other things just make you swear and curse
when you're chewing on life's gristle
don't grumble, give a whistle
and this'll help things turn out for the best
and always look on the bright side of life
always look on the light side of life
if life seems jolly rotten
there's something you've forgotten
and that's to laugh and smile and dance and sing
when you're feeling in the dumps
don't be silly chumps
just purse your lips and whistle, that's the thing
Exercice : modifier un fichier texte (suite)
Modifiez le programme précédent afin que le contenu modifié du fichier ne soit plus affiché mais sauvé dans un autre fichier.
Le chemin du fichier de destination peut être demandé à l’utilisateur ou passé en paramètre du script.
Exercice : QCM en invite de commande (suite)
Reprenons l’exercice sur le QCM en invite de commande. Nous voulons charger les données du QCM à partir d’un document JSON.
[
{
"libelle": "Comment s'appelle le chien de Tintin ?",
"choix": ["Félix", "Gustave", "Milou"],
"reponse": 2
},
{
"libelle": "Combien y a-t-il de points cardinaux ?",
"choix": ["2", "4", "360"],
"reponse": 1
}
]
Vous pouvez télécharger le fichier JSON d'exemple
.
Pour charger un document JSON, vous devrez utiliser le module json
.
Ce dernier fournit notamment la méthode load
qui permet de charger
une document JSON sous la forme d’un dictionnaire Python.