Python : Les fichiers
Ouverture de fichiers
Pour travailler avec le contenu des fichiers (lire ou écrire), il faut « ouvrir » ces fichiers avec la fonction « open() ». Cette fonction prend obligatoirement deux paramètres : le fichier (avec son chemin relatif ou absolu), et le mode d'ouverture. C'est le « mode d'ouverture » qui défini si on va simplement lire le contenu du fichier, ou si on va écrire dedans.
Ici, nous travailons avec un fichiers "liste-courses.txt", qui contient les lignes suivantes :
salade tomates oignons
En lecture
#!/usr/bin/env python3 # ouverture en lecture fichier = open('liste-courses.txt', 'r') # on parcours le contenu du fichier ligne par ligne for ligne in fichier: print(ligne) # il ne faut pas oublier de fermer le fichier fichier.close()
En écriture
Le mode write écrase le contenu du fichier, et le créée si il n'existe pas.
#!/usr/bin/env python3 # ouverture en écriture fichier = open('liste-courses.txt', 'w') # on écrit des éléments dans notre fichier fichier.write('bananes\n') fichier.write('pommes\n') fichier.write('fraises\n') fichier.close()
En append
On peut aussi ouvrir un fichier de façon à écrire à sa suite. Tout comme le mode écriture, un fichier qui n'existait pas est créé. Pratique pour du log !
#!/usr/bin/env python3 # ouverture en écriture (à la suite) fichier = open('liste-courses.txt', 'a') fichier.write('salade\n') fichier.write('tomates\n') fichier.write('oignons\n') fichier.close()
Fonctions
open()
Cette fonction prend en arguments :
-le nom du fichier (chemin absolu ou relatif)
-Le mode d'ouverture (par défaut, 'r')
Le fichier ouvert étant verrouilé, il ne faut oublier de le close().
Lors du parcours du fichier, si une erreur se produit et que le fichier n'est pas fermé, il devient inutilisable dans la suite du programme. Dans certains cas, il est donc préférable d'utiliser la fonction `open()` avec le mot clé `with` qui s'occupera de fermer le fichier en cas d'exception. Dans ce cas il n'est plus nécessaire de fermer explicitement le fichier.
with open('liste-courses.txt', 'r') as fichier: contenu = fichier.read() print(contenu)
Ça fonctionne aussi en écriture :
with open('liste-courses.txt', 'w') as fichier: fichier.write('papier toilette')
Et en append :
with open('liste-courses.txt', 'a') as fichier: fichier.write('dentifrice')
Fonctions de lecture du fichier
On en a plusieurs :
-read() retourne le contenu du fichier dans un string:
contenu = fichier.read()
-readline() retourne la première ligne à son premier appel, la deuxième à son deuxième...
ligne1 = fichier.readline() # ligne 1 ligne2 = fichier.readline() # ligne 2
-readlines() renvoie les lignes du fichier dans une liste :
liste_lignes = fichier.readlines()
-Le mieux reste la boucle for :
for ligne in fichier: […]
Opérations sur les fichiers
Excepté la dernière, toutes ces commandes sont issues du module 'os'.
-Se placer dans un dossier :
os.chdir('/chemin/dossier/')
-Récupérer le répertoire de travail
chemin = os.getcwd()
-Lister le contenu d'un dossier
liste = os.listdir('/chemin/dossier/')
-Renommer un fichier / un dossier:
os.rename('ancien_nom.txt', 'nouveau_nom.txt')
-Supprimer un fichier
os.remove('fichier.txt')
-Créer un dossier
os.mkdir('/chemin/dossier_vide/')
-Supprimer un dossier vide
os.rmdir('/chemin/dossier_vide/')
-Supprimer un dossier et son contenu (avec shutil):
os.rmdir('/chemin/dossier_vide/')
Tests sur les fichiers
Encore une fois, tout est issu du module os.
-Vérifier si le dossier/fichier existe
if os.path.exists('/chemin/fichier.txt'): print('Le fichier existe.')
-Vérifier si c'est un fichier
if os.path.isfile('/chemin/fichier.txt'): print("L'élément est un fichier.")
-Vérifier si c'est un dossier:
if os.path.isdir('/chemin/fichier.txt'): print("L'élément est un fichier.")
-Vérifier si c'est un lien
if os.path.islink('/chemin/symlink'): print("L'élément est un lien.")
Vérifier si il s'agit d'un point de montage :
if os.path.ismount('/chemin/mount_point/'): print("L'élément est un point de montage.")
Plutôt que de faire ce genre de tests, il vaut parfois mieux utiliser un bloc try, ce qui évite d'avoir une co**lle si le fichier est modifié entre l'ouverture et le test :
try: with open('schrodinger-file.txt', 'r') as fichier: contenu = fichier.readlines() except IOError as erreur: print(erreur)
Exemples
Numérotation des lignes d'un fichier
# import du module `os` import os # initialisation d'un compteur pour le numéro de ligne i = 0 # demande de saisie du nom du fichier à traiter nom_fichier = input('Saisir nom du fichier : ') # si le fichier existe if os.path.isfile(nom_fichier): # ouvrir le fichier fichier = open(nom_fichier, 'r') # sinon else: # afficher un message d'erreur print('Erreur : `' + nom_fichier + '` n\'existe pas ' + 'ou n\'est pas un fichier.') # quitter le programme avec un code d'erreur exit(1) # récupérer les lignes du fichier dans une liste contenu = fichier.readlines() # fermer le fichier fichier.close() # parcours de chaque élément de la liste de lignes for ligne in contenu: # incrémentation du numéro de ligne i += 1 # suppression du retour chariot en fin de ligne ligne = ligne.rstrip() # affichage du numéro et de la ligne print(str(i) + '. ' + ligne)
Ce script peut être optimisé de la façon suivante :
-Sécurité : On a remplacé le test `isfile` par un bloc `try: […] except:`. De cette manière si l'élément saisit par l'utilisateur n'est pas un fichier, n'est pas lisible ou est supprimé entre temps, une exception est levée et capturée, plutôt que de faire planter le programme.
-Sécurité : On a utilisé le mot clé `with` pour l'ouverture du fichier. De cette manière, si une erreur se produit lors du parcours du fichier, celui-ci est refermé automatiquement, contrairement à l'ouverture classique, pour laquelle il faut explicitement refermer le fichier avec la méthode `close()`. Sans ça, il serait impossible de réutiliser le fichier dans la suite programme.
-Performance : L'utilisation du bloc `try: […] except:` permet d'éviter l'import du module `os`, et donc de limiter le nombre de lignes, ainsi que l'empreinte mémoire de notre script.
-Performance : On a remplacé l'utilisation de la méthode `readlines()` par un parcours de l'objet avec la boucle `for` directement sur celui-ci (comme s'il s'agissait d'une liste). Sur de petits fichiers le gain est négligeable, mais pour de très gros fichier cette optimisation prend tout son sens.
Bien sûr, il faut savoir s'adapter en fonction de l'utilisation et du contexte.
# initialisation d'un compteur pour le numéro de ligne i = 0 # demande de saisie du nom du fichier à traiter nom_fichier_in = input('Saisir nom du fichier : ') # « essayer » le bloc d'instructions try: # ouvrir le fichier with open(nom_fichier_in, 'r') as fichier: # parcours de l'objet `fichier` for ligne in fichier: # incrémentation du numéro de ligne i += 1 # suppression du retour chariot en fin de ligne ligne = ligne.rstrip() # affichage du numéro et de la ligne print(str(i) + '. ' + ligne) # en cas d'erreur de type `IOError`, la récupérer dans la variable `erreur` except IOError as erreur: # afficher l'erreur print(erreur) # quitter le programme avec un code d'erreur exit(1)
Même exemple avec écriture :
# initialisation d'un compteur pour le numéro de ligne i = 0 # saisies de noms de fichiers nom_fichier_in = input('Saisir nom du fichier source : ') nom_fichier_out = input('Saisir nom du fichier de sortie : ') # les fichiers doivent être différents if nom_fichier_in == nom_fichier_out: print('Impossible d\'utiliser le même fichier en entrée et en sortie !') exit(1) try: # ouvertures fichier_in = open(nom_fichier_in, 'r') fichier_out = open(nom_fichier_out, 'w') # parcours `fichier_in` for ligne in fichier_in: i += 1 # écriture `fichier_out` fichier_out.write(str(i) + '. ' + ligne) # fermetures fichier_in.close() fichier_out.close() except IOError as erreur: print(erreur) exit(1)
À ne pas faire...!
Ouvrir en écriture dans une boucle
# ouvertures fichier_in = open(nom_fichier_in, 'r') # parcours `fichier_in` for ligne in fichier_in: # écriture `fichier_out` fichier_out = open(nom_fichier_out, 'w') fichier_out.write(ligne) fichier_out.close() # fermetures fichier_in.close()
Ici, on écrase le fichier à chaque itération de la boucle, et à la fin on aura que la fin du fichier...
À savoir...
La fonction `open()` renvoie un objet de la classe `TextIOWrapper`, qui elle-même hérite de `TextIOBase`. Ce sont les méthodes de ces classe que nous utilisons pour interagir avec le fichier. Il en existe d'autre que celles que nous avons vu, vous pouvez les retrouver dans la documentation Python3.
On peut écrire et sauvegarder des objets dans des fichiers (on écrit en binaire) avec le module pickle pour les retrouver après. C'est vachement utile.