Python : Les fichiers

De Justine's wiki
Aller à la navigation Aller à la recherche

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.