{{tag>cours python dev todo}} ====== Python: les fichiers ====== Les **fichiers** sont, comme souvent en Python, simples et intuitifs à utiliser, cependant il faut maîtriser trois notions que sont **l'encodage, l'itération et la notion de context manager**. Ouvrons maintenant un interpréteur Python pour commencer à jouer avec ces différentes notions. Je vous rappelle que lorsque nous avons parlé des chaînes de caractères, je vous avais dit qu'il était très important de maîtriser votre encodage, ce qui veut dire que vous devez maîtriser l'encodage pour **écrire** les chaînes de caractères mais également pour les **lire**. Nous allons voir qu'avec les fichiers, cette gestion de l'encodage est extrêmement simple puisque c'est l'objet fichier qui va se charger d'encoder et de décoder les chaînes de caractères pour nous. Regardons ça avec un exemple. Nous allons créer un fichier avec l'instruction **built-in open**, avec la fonction built-in open. Créons un fichier donc je fais une variable f qui est égale à open et je vais ouvrir un fichier, alors je vais dire que mon fichier est dans le répertoire *c:\temp\spam.txt*. Il y a un point très important à respecter que vous avez dû voir dans les compléments c'est que les chaînes de caractères avec des backslashes (\) vont interpréter les backslashes, notamment *\t* est un caractère de tabulation. Donc si je ne veux pas avoir d'interprétation soit je dois doubler tous les backslashes ou alors la manière recommandée, c'est de transformer votre chaîne en **rawstring** en mettant un petit *r* ce qui va désactiver tous les caractères *\.* Maintenant que j'ai défini mon nom de fichier je vais donner un mode d'ouverture. Les modes d'ouverture les plus courants sont le mode *w*pour ouvrir le fichier en écriture ou le mode *r*pour ouvrir le fichier en lecture. Et ensuite, comme j'ai ouvert mon fichier en mode texte, nous verrons dans la deuxième partie de cette vidéo l'ouverture des fichiers en mode binaire, je vais spécifier un encodage. Je vais donc mettre un **paramètre encoding**et quel encoding je vais mettre ? je vous l'ai déjà expliqué, lorsque je ne sais pas quoi mettre, par défaut, je mets toujours de l'**utf8**. Je fais un retour chariot, j'ai donc ouvert un fichier en mode écriture ; si le fichier n'existe pas il vient d'être créé sur mon disque dur, avec un encodage utf8. Maintenant, je vais pouvoir manipuler le fichier de manière extrêmement simple, je n'ai plus à me préoccuper de l'encodage, je vais écrire dans mon fichier des chaînes de caractères Python de type *str*, et après, je pourrai lire des chaînes de caractères de mon fichier. L'objet fichier va se charger pour nous de faire l'encodage et le décodage. Maintenant, regardons comment écrire dans un fichier. Je vais simplement faire une boucle *for i in range de 100*: et je vais faire un **f.write(f"ligne {i+1}\n")**. Donc ici, nous voyons que je fais une boucle *for* qui va parcourir tous les entiers allant de 0 à 99, et je vais, avec l'instruction write, écrire dans mon fichier une chaîne de caractères. Donc ici ma chaîne de caractères est une *f-string*, mais c'est une chaîne de caractères de type *str*, une chaîne de caractères normale. Il est très important de ne pas oublier le backslash n, qui permet de faire le retour chariot. Si vous ne faites pas de *retour chariot*, tous vos *write* vont écrire à la suite de la ligne. Donc pour faire écrire sur une ligne différente à chaque fois, il faut obligatoirement mettre de manière explicite un *\n*. Donc je fais un retour chariot et maintenant, j'ai écrit dans mon objet fichier. Pour finir, il ne faut pas oublier de fermer le fichier avec un **f.close**. Nous allons revenir dans quelques instants sur l'importance de cette manière de fermer les fichiers, et surtout sur la manière moderne de manipuler les fichiers que l'on appelle les **context managers**. Regardons maintenant le fichier qui a été créé. Si vous êtes dans un interpréteur IPython, vous pouvez très facilement lancer des commandes shell avec la commande !. Comme je suis sous Windows je vais taper *type* pour voir mon fichier mais si vous êtes sous Linux ou sous macOS, vous pouvez très bien utiliser less ou cat. Je vais maintenant regarder mon fichier. Je regarde avec type, type, c'est juste pour regarder le contenu du fichier, mon fichier temp/spam.txt et je vois mon fichier apparaître avec le mot ligne et le numéro qui va de 1 à 100. Vous avez remarqué qu'une fois que mon fichier a été ouvert, je n'ai plus à me préoccuper de l'encodage, mon fichier a bien été écrit correctement et a bien été décodé correctement. Donc maintenant regardons comment lire un fichier et réécrire dans un nouveau fichier. Nous allons voir qu'en Python le parcours des fichiers est également extrêmement simple puisqu'en Python, **les fichiers sont des itérateurs**. Ça veut dire qu'on peut directement les mettre dans une boucle for . Regardons cela. Je vais ouvrir un fichier f, je vais réouvrir le fichier que je viens de créer *c:\temp\spam.txt* ; je vérifie que c'est bien le fichier que j'ai créé voilà. Je n'oublie pas de spécifier le mode d'ouverture donc ici, je vais ouvrir mon fichier en mode lecture simple, c'est spécifié avec un petit *r*, et je n'oublie pas de spécifier l'encodage encoding égale utf8 puisque j'ai créé mon fichier en utf8, il faut que je le lise et que je l'écrive évidemment avec le même encodage. Je vais ensuite créer un deuxième fichier f2 avec **open**, je vais reprendre, ça va être plus simple, je vais reprendre ma ligne du dessus. Je vais ouvrir un fichier spam2, qui est le fichier sur lequel je vais écrire, j'ouvre ce fichier en mode écriture avec le mode d'ouverture *w* et je gère encore mon encodage utf8. Maintenant, je vais parcourir mon premier fichier f, je vais faire une petite manipulation des lignes, et je vais écrire dans mon fichier f2. Regardons cela. Pour cela, je vais faire une boucle for et je vais faire un *for line in f*: que va faire la boucle for sur un fichier ? Lorsque le fichier est ouvert, il va être parcouru ligne par ligne donc Python va être capable de détecter automatiquement les sauts de ligne, et il va me retourner à chaque tour de la boucle for, une nouvelle ligne. C'est donc une manière simple et extrêmement intuitive de parcourir les lignes d'un fichier. Donc je fais for line in f et ensuite, supposons que je veuille simplement changer le formatage de mon fichier, c'est-à-dire, qu'en fait, au lieu d'avoir des espaces qui séparent mes deux colonnes, je souhaite avoir une virgule. Regardons comment faire ça. La première chose à faire lorsqu'on parcourt les lignes d'un fichier, très souvent, c'est de transformer cette chaîne de caractères en liste. Nous avons déjà vu qu'on pouvait transformer une chaîne de caractères en liste avec **la méthode split** sur les chaînes de caractères. Donc je fais un **line.split**, je transforme ma ligne en objet liste, où le premier élément va être le mot ligne, le deuxième élément va être le numéro de la ligne. Et ensuite, je vais faire, alors, je peux tout à fait transformer, pour vous montrer cette possibilité, ma première colonne ligne en mot majuscule. Je le mets en majuscule, mon objet line étant une liste, il est mutable, je peux donc le modifier en place, et ensuite, je vais écrire dans mon fichier f2 avec l'instruction write, je va s faire un join pour transformer ma liste en chaîne de caractères, je mets une virgule pour maintenant avoir une virgule qui sépare mes colonnes, .join de line Et je n'oublie pas de rajouter un \n pour avoir mon retour chariot à chaque ligne. Donc j'ai écrit simplement, j'ai fait un join de ma liste en la séparant par une virgule, et j'ai rajouté un \n à la fin de ma ligne. J'exécute cela. Je n'oublie pas de fermer mes fichiers f et f2, et maintenant, je peux regarder mes fichiers, comme tout à l'heure avec mon point d'exclamation parce que je suis sur IPython ; évidemment, si vous n'utilisez pas IPython vous regardez directement les fichiers avec votre éditeur de texte préféré sur votre disque dur. Je vais regarder mon fichier qui est dans le répertoire temp et qui s'appelle spam.txt, regardons ce fichier qui est le même fichier que tout à l'heure et je regarde mon fichier spam2 et je remarque que mon fichier a bien été modifié comme attendu, la première colonne est en majuscule et mes deux colonnes sont maintenant séparées par une virgule. Vous voyez donc qu'il est extrêmement simple d'ouvrir, de manipuler un fichier, de parcourir ses lignes, de modifier les lignes, et d'écrire dans un nouveau fichier. # W3-S1 Les fichiers : 2ème partie Nous venons de voir qu'il était extrêmement important de bien fermer vos fichiers. Cependant, vous pouvez vous demander mais pourquoi est-ce tellement important de fermer les fichiers ? En fait, la raison est simple. Votre programme Python va discuter avec le système d'exploitation et c'est le système d'exploitation qui va effectivement ouvrir, fermer et écrire dans les fichiers. Lorsque vous faites un open, vous dites à votre système d'exploitation: attention, moi, je veux ouvrir ce fichier ; et lorsque vous faites un close, vous lui dites: je veux fermer ce fichier. Comme le nombre de fichiers simultanément ouverts est limité dans un système d'exploitation, si vous ne dites pas que vous faites un *close*, le système d'exploitation va croire que le fichier est toujours ouvert, et vous pouvez vous retrouver dans une situation dans laquelle votre ordinateur n'est plus capable d'ouvrir de nouveaux fichiers. Mais quand on y pense, on se dit mais pourquoi est-ce que c'est au programmeur de penser à fermer systématiquement le fichier, alors qu'en Python, tout est un objet et qu'un fichier est également un objet. On pourrait très bien se dire : en fait, on pourrait faire en sorte que l'objet soit suffisamment intelligent pour savoir que lorsqu'on n'en a plus besoin il fait toutes les opérations nécessaires à sa fermeture. Et bien en fait, ce mécanisme existe en Python. C'est quelque chose qu'on appelle un protocole qui s'appelle le **protocole de context manager**. En fait, un objet fichier implémente ce protocole de context manager, et, lorsque l'on n'a plus besoin de ce fichier, le fichier sera automatiquement fermé. Regardons comment cela fonctionne en Python. Pour accéder à ce **protocole de context manager**, vous utilisez l'**instruction with**, ensuite, vous ouvrez votre fichier normalement, comme un fichier standard, on va ouvrir le fichier c:/temp/spam.txt. Ensuite, j'ouvre mon fichier en mode lecture et évidemment, je contrôle l'encodage avec *encoding égale utf8*. Ensuite, je vais donner un nom à cet objet fichier, *as f*, et je mets un *: *ça veut dire que je vais créer un bloc d'instructions. Dans mon context manager, j'ai un bloc d'instructions et ce bloc d'instructions va être exécuté ; lorsque je sors de ce bloc d'instructions, on va appeler une méthode qui s'appelle*exit *sur le context manager de l'objet fichier, nous verrons ça dans de prochaines vidéos, qui va avoir pour effet de fermer automatiquement le fichier. L'intérêt de ce *context manager*, c'est que vous n'avez plus à fermer explicitement le fichier, il sera automatiquement fermé en sortie de bloc de code, mais c'est aussi que si vous avez une exception dans ce bloc de code, le fichier sera quand même fermé. Cela simplifie énormément l'ouverture des fichiers puisque vous n'avez plus à gérer la fermeture et les erreurs d'exécution. Maintenant, parcourons mon fichier, *for line in f :*, et je vais simplement faire un *print(line)*. J'affiche les différentes lignes de mon fichier. J'exécute ce **context manager**, et je vois les différentes lignes s'afficher. En sortie du bloc de code, le fichier a été automatiquement fermé et encore une fois, si j'avais une exception le fichier aurait été fermé. Regardons maintenant comment écrire dans un **fichier binaire**. Comme nous venons de découvrir les context managers, c'est ce que nous allons utiliser par la suite. Nous allons ouvrir notre fichier dans un context manager, *with* je mets la fonction built-in *open*, et je vais ouvrir mon fichier c:\temp\spam.bin. Ensuite, je vais spécifier le mode d'ouverture je veux ouvrir mon fichier en mode écriture, et pour dire à Python que je veux l'ouvrir en mode binaire, je n'ai qu'à rajouter un petit *b*au mode d'ouverture du fichier. Donc si je veux ouvrir un fichier en **mode binaire et écriture**, j'écris *bw*; si je veux l'ouvrir en **mode binaire et lecture**, je vais écrire *br*. Comme mon fichier est ouvert en mode binaire, je n'ai pas spécifié d'encodage, et ensuite j'écris*as f: *pour donner un nom à mon fichier. Ensuite, je vais faire une simple boucle : *for i in range de 100*, et je vais écrire dans mon fichier *f.write*. Comme mon fichier est ouvert en mode binaire, je ne peux écrire dans ce fichier que des objets de **type bytes**et je ne pourrai lire que des objets de type bytes. Pour écrire un objet de type bytes, c'est très simple, j'écris une chaîne de caractères en écrivant un petit b juste avant. Ça, c'est une chaîne de caractères de type bytes, et dedans, je vais écrire un caractère hexadécimal qui est juste avant \x3d. J'exécute mon bloc de code, j'ai écrit dans mon fichier, en sortie du bloc de code, le context manager a fermé le fichier, et j'ai maintenant sur mon disque dur un fichier qui s'appelle spam.bin qui contient le caractère 3d écrit 100 fois. Donc pour résume encore une fois, ouverture d'un fichier en mode texte: on manipule le fichier avec des objets de type **str**; ouverture d'un fichier en mode binaire: on manipule le fichier avec des objets de type **bytes**. Nous venons de voir les fichiers en Python. Nous avons vu que ces objets fichiers sont des objets extrêmement puissants, qui permettent de manière naturelle et intuitive de parcourir des fichiers. On a également vu la notion de context manager, qui permet d'ouvrir un fichier et de garantir sa fermeture automatique lorsqu'on n'a plus besoin de cet objet fichier. Par la suite, nous vous recommandons d'utiliser systématiquement les context managers dès que vous aurez à ouvrir des fichiers en Python.