6.2. Les objets-fichier

Python a une fonction prédéfinie, open, pour ouvrir un fichier sur le disque. open retourne un objet-fichier qui possède des méthodes et des attributs pour obtenir des informations et manipuler le fichier ouvert.

Exemple 6.3. Ouverture d'un fichier

>>> f = open("/music/_singles/kairo.mp3", "rb") 1
	>>> f                                           2
	<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.mode                                      3
'rb'
>>> f.name                                      4
'/music/_singles/kairo.mp3'
1 La méthode open peut prendre jusqu'à trois paramètres : un nom de fichier, un mode et un paramètre de tampon. Seul le premier, le nom de fichier, est nécéssaire, les deux autres sont optionnels. Si le mode n'est pas spécifié, le fichier est ouvert en mode texte pour la lecture. Ici nous ouvrons le fichier en mode binaire pour la lecture (print open.__doc__ affiche une bonne explication de tous les modes possibles).
2 La fonction open retourne un objet (arrivé à ce point cela ne doit pas vous surprendre). Un objet-fichier à plusieurs attributs utiles.
3 L'attribut mode d'un objet-fichier vous indique dans quel mode le fichier a été ouvert.
4 L'attribut name d'un objet-fichier vous indique le nom du fichier qui a été ouvert.

6.2.1. Lecture d'un fichier

Une fois un fichier ouvert, la première chose que l'on peut faire est de le lire, comme nous allons le voir dans l'exemple suivant.

Exemple 6.4. Lecture d'un fichier

>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.tell()              1
0
>>> f.seek(-128, 2)       2
>>> f.tell()              3
7542909
>>> tagData = f.read(128) 4
>>> tagData
'TAGKAIRO****THE BEST GOA         ***DJ MARY-JANE***            
Rave Mix                      2000http://mp3.com/DJMARYJANE     \037'
>>> f.tell()              5
7543037
1 Un objet-fichier maintien des informations d'état sur le fichier qui est ouvert. La méthode tell d'un objet-fichier vous indique la position actuelle dans le fichier ouvert. Comme nous n'avons encore rien fait de ce fichier la position actuelle est 0, le début du fichier.
2 La méthode seek d'un objet-fichier permet de se déplacer dans le fichier ouvert. Le deuxième paramètre précise ce que le premier signifie : 0 pour un déplacement à une position absolue (en partant du début du fichier), 1 pour une position relative (en partant de la position actuelle) et 2 pour une position relative à la fin du fichier. Puisque les balises MP3 que nous recherchons sont stockés à la fin du fichier, nous utilisons 2 et nous déplaçons à 128 octets de la fin du fichier.
3 La méthode tell confirme que la position actuelle a changé.
4 La méthode read lit un nombre d'octets spécifié du fichier ouvert et retourne une chaîne contenant les données lues. Le paramètre optionnel précise le nombre maximal d'octets à lire. Si aucun paramètre n'est spécifié, read lit jusqu'à la fin du fichier. (Nous aurions pu taper simplement read() ici, puisque nous savons exactement où nous sommes dans le fichier et que nous lisons en fait les 128 derniers octets.) Les données lues sont assignées à la variable tagData et la position actuelle est mise à jour en fonction du nombre d'octets lus.
5 La méthode tell confirme que la position actuelle a changé. Si vous faites le calcul, vous verrez qu'après que nous ayons lu 128 octets, la position a été incrémenté de 128.

6.2.2. Fermeture d'un fichier

Les fichiers ouverts consomment des ressources système et, en fonction du mode d'ouverture, peuvent ne pas être accessibles à d'autres programmes. Il est donc important de fermer les fichiers dès que vous ne les utilisez plus.

Exemple 6.5. Fermeture d'un fichier

>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed       1
False
>>> f.close()      2
>>> f
<closed file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed       3
True
>>> f.seek(0)      4
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.tell()
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.read()
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.close()      5
1 L'attribut closed d'un objet-fichier indique si l'objet pointe un fichier ouvert ou non. Dans ce cas, le fichier est toujours ouvert (closed vaut False).
2 Pour fermer un fichier, appelez la méthode close de l'objet-fichier. Cela libère le verrou (s'il existe) que vous avez sur le fichier, purge les tampons en écriture (s'ils existent) et libère les ressources système.
3 L'attribut closed confirme que le fichier est fermé.
4 Ce n'est pas parce que le fichier est fermé que l'objet fichier cesse d'exister. La variable f continuera d'exister jusqu'à ce qu'elle soit hors de portée ou qu'elle soit supprimée manuellement. Cependant aucune des méthodes de manipulation d'un fichier ouvert ne marchera après que le fichier ait été fermé, elles déclencheront toutes une exception.
5 Appeler close sur un objet-fichier dont le fichier est déjà fermé ne déclenche pas d'exception mais échoue silencieusement.

6.2.3. Gestion des erreurs d'entrée/sortie

Maintenant vous en avez vu assez pour comprendre le code de gestion de fichier dans le programme d'exemple fileinfo.py du chapitre précédent. L'exemple suivant montre comment ouvrir et lire un fichier de manière sûre en gérant les erreurs.

Exemple 6.6. Les objets-fichier dans MP3FileInfo

        try:                                1
            fsock = open(filename, "rb", 0) 2
            try:                           
                fsock.seek(-128, 2)         3
                tagdata = fsock.read(128)   4
            finally:                        5
                fsock.close()              
            .
            .
            .
        except IOError:                     6
            pass                           
1 Comme l'ouverture et la lecture de fichiers est risquée et peut déclencher une exception, tout ce code est enveloppé dans un bloc try...except (alors, l'indentation standardisée n'est-elle pas admirable ? C'est là que l'on commence a vraiment l'apprécier).
2 La fonction open peut déclencher une exception IOError (peut-être que le fichier n'existe pas).
3 La méthode seek peut déclencher une exception IOError (peut-être que le fichier fait moins de 128 octets).
4 La méthode read peut déclencher une exception IOError (peut-être que le disque a un secteur défectueux ou le fichier est sur le réseau et le réseau est en rideau).
5 Voilà qui est nouveau : un bloc try...finally. Une fois le fichier ouvert avec succès par la fonction open, nous voulons être absolument sûrs que nous le refermons, même si une exception est déclenchée par les méthodes seek ou read. C'est à cela que sert un bloc try...finally : le code du bloc finally sera toujours exécuté, même si une exception est déclenchée dans le bloc try. Pensez-y comme à du code qui est exécuté «au retour», quoi qu'il se soit passé «en route».
6 Enfin, nous gérons notre exception IOError. Cela peut être l'exception IOError déclenchée par l'appel à open, seek, ou read. Ici, nous ne nous en soucions vraiment pas car tout ce que nous faisons et d'ignorer l'erreur et de continuer (rappelez-vous que pass est une instruction Python qui ne fait rien). C'est tout à fait légal, «gérer» une exception peut vouloir dire explicitement ne rien faire. Cela compte quand même comme une exception gérée et le traitement va reprendre normalement à la prochaine ligne de code après le bloc try...except.

6.2.4. Ecriture dans un fichier

Nous pouvons bien sûr écrire dans un fichier, cela se fait en grande partie de la même manière que pour la lecture. Il y a deux modes d'ouverture de base :

  • Le mode Append (ajout) pour ajouter des données à la fin du fichier.
  • Le mode Write (écriture) pour écraser le contenu du fichier.

Les deux modes créeront le fichier automatiquement s'il n'existe pas encore, il n'y a donc pas besoin de logique du type «si le fichier de journalisation n'existe pas, créer un nouveau fichier vide et l'ouvrir en écriture.» Il suffit d'ouvrir le fichier pour commencer à y écrire.

Exemple 6.7. Ecriture dans un fichier

>>> logfile = open('test.log', 'w') 1
>>> logfile.write('test succeeded') 2
>>> logfile.close()
>>> print file('test.log').read()   3
test succeeded
>>> logfile = open('test.log', 'a') 4
>>> logfile.write('line 2')
>>> logfile.close()
>>> print file('test.log').read()   5
test succeededline 2
1 Nous commençons par créer un nouveau fichier test.log ou l'écraser s'il existe et l'ouvrir en écriture (le second paramètre "w" signifie ouverture en écriture). Effectivement, c'est aussi dangeureux que ça en a l'air. J'espère que vous n'aviez rien de précieux dans ce fichier, parce que maintenant c'est effacé.
2 Vous pouvez écrire dans le fichier ouvert à l'aide de la méthode write de l'objet-fichier retourné par open.
3 file est un synonyme de open. Ici, nous ouvrons le fichier, lisons son contenu et l'imprimons en une seule ligne.
4 Nous savons que test.log existe (puisque nous venons juste d'écrire dedans), donc nous pouvons l'ouvrir pour ajouter des données (le paramètre "a" signifie ouverture pour ajout). En fait, nous pourrions le faire même si le fichier n'existait pas, puisque l'ouverture du fichier en mode ajout crée le fichier si nécéssaire. Mais le mode ajout n'endommagera jamais le contenu du fichier.
5 Comme vous pouvez le voir, la ligne d'origine aussi bien que la nouvelle ligne ajoutée sont maintenant dans test.log. Notez également que le retour à la ligne n'est pas inclus. Puisque nous n'en avons pas explicitement écrit dans le fichier, le fichier n'en contient pas. Nous pouvons écrire un retour à la ligne avec le caractère "\n". Puisque nous ne l'avons pas fait, tout ce que nous avons écrit finit sur la même ligne.

Pour en savoir plus sur l'utilisation de fichiers