{ "metadata": { "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3-final" }, "orig_nbformat": 2, "kernelspec": { "name": "python37364bit201329ed7ee849b4a43ad7bb610f5989", "display_name": "Python 3.7.3 64-bit" } }, "nbformat": 4, "nbformat_minor": 2, "cells": [ { "source": [ "# Gestion des fichiers avec Python\n", "\n", "Pour ce notebook il faut placer dans le dossier **data** le fichier : **fable.txt**\n", "\n", "*Objectifs :* \n", " - ouvrir un fichier\n", " - lire son contenu\n", " - enregistrer un fichier\n", "\n", "*Utilisation :* Ce notebook permet de découvrir la manipulation des fichiers avec Python. La quasi totalité des cellules de code sont fonctionnelles. Il s'agira essentiellement de les exécuter pour aller voir le résultat obtenu. Bien lire les commentaires associés.\n", "\n", "## 1-\"Ouvrir un fichier\"\n", "\n", "Lorsque l’on est **utilisateur** d’un logiciel, on rencontre essentiellement trois actions concernant un fichier : * Nouveau *, * Ouvrir * et *Enregistrer * et éventuellement * Enregistrer sous *.\n", "L’option * Ouvrir * consiste alors à aller chercher un fichier déjà existant sur un périphérique de sauvegarde (disque dur, clé USB, serveur…) et en faire apparaître le contenu à l’intérieur du logiciel.\n", "\n", "**En programmation** le terme ouvrir un fichier recouvre une signification plus large.\n", " En Python, la fonction native s’appelle **open( )** et elle peut prendre plusieurs paramètres (jusqu’à 8 !). \n", " \n", " Dans une première approche, on n’utilisera que les deux premiers, pour l’utiliser sous la forme : \n", " \n", " **open(‘chemin/nom_du_fichier’ , ‘mode d’ouverture’)**\n", "\n", " • ‘chemin/nom_du_fichier’ : chaîne de caractère qui permet de localiser le fichier. Si on ne donne que le nom du fichier,\n", " alors ce fichier devra se trouver dans le répertoire courant.\n", " \n", " • ‘mode d’ouverture’ : un ou plusieurs caractères selon le tableau suivant :\n", "\n", "![mode_ouverture.png](images/mode_ouverture.png)\n", "\n", "L’utilisation de la fonction open(), qui sera la première fonction à appeler, nécessite de **savoir par avance ce que l’on veut faire…**\n", "Si on omet de renseigner le paramètre ‘mode d’ouverture’, alors par défaut on ouvrira le fichier spécifié en lecture et en mode texte.\n", "Cette fonction **open()** renvoie un *objet fichier*, elle s’utilisera donc sous la forme :\n", "```python\n", "fichier = open(‘chemin/nom_du_fichier’ , ‘mode d’ouverture’)\n", "```\n", "dans laquelle *fichier* est le *nom de la variable* que l’on attribuera à cet ‘objet fichier’\n", "\n", "Cet *objet fichier* pourra être manipulé ensuite avec différentes méthodes telles que :\n", " - read(),\n", " - readline()\n", " - readlines() \n", " - write()\n", " \n", "**IMPORTANT :** Une fois que l’on aura fini le traitement des différentes instructions liées au fichier il sera impératif de **fermer l’objet fichier** en utilisant la méthode **close()** de façon à libérer des ressources systèmes :\n", "```python \n", "fichier.close()\n", "```" ], "cell_type": "markdown", "metadata": {} }, { "source": [ "## 2- Lire un fichier texte\n", "\n", "On cherche ici à lire le contenu d’un fichier texte existant. Vérifier que le fichier **fable.txt** est présent dans le dossier **data**. Il s'agit d'un texte de Jean de La Fontaine bien connu…\n", "\n", "### 2.1- La méthode read(size) :\n", "Le paramètre size est optionnel." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Utilisation de la méthode read(), le paramètre size étant omis :\n", "fichier = open('data/fable.txt','r')\n", "contenu = fichier.read()\n", "# ne pas oublier :\n", "fichier.close()\n", "\n", "# ... et maintenant on travaille sur ce que l'on a récupéré :\n", "print(type(contenu))\n", "print(contenu) \n" ] }, { "source": [ " - quel est le type de la variable *contenu* ? C'est important de le repérer pour savoir comment manipuler cette variable par la suite !\n", " - que renvoie la méthode *read()* lorsque le paramètre *size* est omis ?" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Utilisation de la méthode read(), le paramètre size étant réglé à la valeur 10 :\n", "fichier = open('data/fable.txt','r')\n", "contenu = fichier.read(10)\n", "# ne pas oublier :\n", "fichier.close()\n", "\n", "# ... et maintenant on travaille sur ce que l'on a récupéré :\n", "print(type(contenu))\n", "print(contenu) " ] }, { "source": [ "- que renvoie la méthode *read(size)* lorsque le paramètre *size* est utilisé ? (modifier ce paramètre : par exemple l'augmenter de façon significative...)" ], "cell_type": "markdown", "metadata": {} }, { "source": [ "### 2.2- La méthode readline(size) :\n", "\n", "Elle permet de lire une ligne ou une partie d’une ligne d’un fichier texte\n", "\n", "Le paramètre size est optionnel." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Utilisation de la méthode readline(), le paramètre size étant omis :\n", "fichier = open('data/fable.txt','r')\n", "ligne = fichier.readline()\n", "# ne pas oublier :\n", "fichier.close()\n", "\n", "print(type(ligne)) # cet affichage nous permet de savoir quel est le type de la variable 'ligne'\n", "print(ligne) " ] }, { "source": [ " - quel est le type de la variable *ligne* ? C'est important de le repérer pour savoir comment manipuler cette variable par la suite !\n", " - que renvoie la méthode *readline()* lorsque le paramètre *size* est omis ?" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Utilisation de la méthode readline(size), le paramètre size étant réglé à la valeur 10 :\n", "fichier = open('data/fable.txt','r')\n", "ligne = fichier.readline(10)\n", "# ne pas oublier :\n", "fichier.close()\n", "\n", "print(type(ligne))\n", "print(ligne) " ] }, { "source": [ " - que renvoie la méthode *readline()* lorsque le paramètre *size* est utilisé ? (modifier ce paramètre : par exemple l'augmenter pour qu'il dépasse le nombre de caractères de la ligne *Maître Corbeau, sur un arbre perché,*)" ], "cell_type": "markdown", "metadata": {} }, { "source": [ "*Remarque* : La fin d’une ligne dans un fichier texte est spécifiée par le caractère non imprimable **0x0A** (en hexadécimal). Dans le code ASCII il correspond à **LF** pour *Line Feed* (qui signifie passer à la ligne suivante)\n", "\n", "Ce code est également noté **\\n** en Python ( \\ est le *caractère d’échappement* (pour signifier que le caractère suivant ne doit pas être pris dans son rôle habituel) et *n* est la symbolique pour *newline*).\n", "\n", "Si on ouvre le fichier **fable.txt** avec un éditeur héxadécimal, on pourra repérer dedans cet octet (**0A**) à chaque de fin de ligne :\n", "\n", "![fable_hexadec.png](images/fable_hexadec.png)\n" ], "cell_type": "markdown", "metadata": {} }, { "source": [ "#### Lire l'intégralité du fichier avec readline() :\n", "\n", "La méthode readline() renvoie une chaîne de caractères. Si cette chaîne est vide c’est que l’on a atteint la fin du fichier. En se basant sur cette propriété, modifier le programme pour lire l'intégralité du fichier **fable.txt** ligne par ligne à l’aide d’une boucle while :" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# lecture de toutes les lignes du fichier avec readline():\n", "fichier = open('data/fable.txt','r')\n", "ligne = ' '\n", "while ???\n", " ???\n", " ...\n", " ???\n", "fichier.close()\n" ] }, { "source": [ "Dans la cellule ci-dessous, reprendre le code précédent pour afficher la fable avec la numérotation des lignes.\n", "\n", "Exemple de résultat :\n", "\n", "1 \t Maître Corbeau, sur un arbre perché,\n", "\n", "2 \t Tenait en son bec un fromage.\n", "\n", "3 \t Maître Renard, par l'odeur alléché,\n", "\n", "4 \t Lui tint etc ...\n" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# lecture de toutes les lignes du fichier avec readline() et ajout de la numérotation :\n", "fichier = open('data/fable.txt','r')\n", "ligne = ' '\n", "???\n", "...\n", "???\n", "\n", "fichier.close()" ] }, { "source": [ "#### Caractère itérable d'un objet fichier :\n", "\n", "Remarque : l’objet fichier étant itérable, on peut le parcourir avec l’instruction for … in … .\n", "Tester le code suivant, puis le modifier pour ajouter la numérotation des lignes" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fichier = open('data/fable.txt','r')\n", "\n", "for ligne in fichier:\n", " print(ligne, end='')\n", " \n", "fichier.close()" ] }, { "source": [ "### 2.3-La méthode readlines(size) :\n", "\n", "**Bien remarquer la lettre 's' qui distingue cette méthode de la précédente ! ** \n", "\n", "Le 's' de readlines nous incite à penser que cette méthode permet de lire plusieurs lignes du fichier texte.\n", "\n", "Le paramètre size est optionnel.\n", "\n", "Mettre en oeuvre cette méthode en précisant ou pas une valeur pour le paramètre *size* :" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fichier = open('data/fable.txt','r')\n", "contenu = ???\n", "fichier.close()\n", "\n", "print(type(contenu))\n", "print(contenu) \n" ] }, { "source": [ " - on constate ici que readlines() renvoie une **liste**.\n", " - compléter le code ci-dessus pour afficher le type d'un élément de cette liste\n", "\n", "** Remarque importante :** \n", "Les différentes méthodes de lecture à disposition **renvoient des types différents**. Cette *\"préparation\" initiale* des données aura une incidence sur le traitement à mettre en oeuvre par la suite. \n", "\n", "**Il est donc nécessaire de bien réfléchir à ce que l'on veut faire avec les données présentes dans le fichier avant d'envisager l'une ou l'autre de ces méthodes !**" ], "cell_type": "markdown", "metadata": {} }, { "source": [ "## 3- Ecrire dans un fichier texte :\n", "\n", "Pour écrire dans un fichier texte, il est nécessaire de l’ouvrir en choisissant le bon mode d’ouverture. \n", "\n", "Il faut aussi se poser la question : le fichier existe-t-il déjà ou bien allons nous le créer ?\n", "\n", "### 3.1- Créer un fichier et écrire du texte dedans :\n", "\n", "Le mode **‘w’** permet de *créer un fichier qui n’existe pas et d’écrire dedans*.\n", " \n", " Attention : s’assurer de donner au nom du fichier, un nom de fichier qui **n’existe pas** dans le répertoire de travail, **sinon il sera écrasé**. \n", " \n", " Pour les essais qui sont proposés ci-dessous, on crée un fichier nommé **essai_ecriture.txt** dans le dossier **data**.\n", " On pourra garder successivement le même nom de fichier (en sachant que le contenu précédent sera perdu) sinon en changer le nom. \n", " Observer le résultat obtenu en ouvrant ce fichier **essai_ecriture.txt** à l’aide d'un éditeur de texte (exemple *gedit* sous Linux) et noter vos observations." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# premier essai :\n", "fichier = open('data/essai_ecriture.txt','w')\n", "fichier.write('ok !')\n", "fichier.close()" ] }, { "source": [ "Pour voir le résultat de l'exécution de la cellule ci-dessus, ouvrir le dossier **data** : on doit y trouver le fichier créé. L'ouverture avec un éditeur de texte permet d'en vérifier le contenu :\n", "\n", "![ecriture_fichier.png](images/ecriture_fichier.png)" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Deuxième essai d'écriture :\n", "fichier = open('data/essai_ecriture.txt','w')\n", "fichier.write('Première ligne')\n", "fichier.write('Deuxième ligne')\n", "fichier.close()" ] }, { "source": [ "Ce deuxième essai ne donne pas ce que l'on souhaitait, à savoir l'écriture de deux lignes distinctes de texte :\n", "\n", "![essai_ecriture2.png](images/essai_ecriture2.png)\n", "\n", "Les deux écritures réalisées avec la méthode **write()** sont réaliséess l'une à la suite de l'autre.\n", "\n", "L'observation dans un éditeur hexadécimal:\n", "\n", "![essai_ecriture2_hexa.png](images/essai_ecriture2_hexa.png)\n", "\n", "montre la raison : à la fin de la première ligne, le caractère ASCII de nouvelle ligne (LF, codé 0A en hexadécimal) n'est pas présent. On va l'ajouter (en Python : \\n)" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Deuxième essai d'écriture CORRIGÉ:\n", "fichier = open('data/essai_ecriture.txt','w')\n", "fichier.write('Première ligne\\n') # l'ajout de la séquence d'échappement \\n permet le passage à la ligne\n", "fichier.write('Deuxième ligne')\n", "fichier.close()" ] }, { "source": [ "On obtient bien le résultat attendu :\n", "\n", "![essai_ecriture2_resultat.png](images/essai_ecriture2_resultat.png)" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Remarque : on peut simplifier le code avec une seule instruction write() :\n", "fichier = open('data/essai_ecriture.txt','w')\n", "fichier.write('Première ligne\\nDeuxième ligne ???')\n", "fichier.close()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "source": [ "### 3.2- Créer un fichier, écrire du texte dedans et lire son contenu :\n", "\n", "*Remarque :* on utilise ici un nouveau nom de fichier pour chaque cellule **essai_ecriture_V2.txt** puis **essai_ecriture_V3.txt** ...\n", "\n", "#### 3.2.1- Premiers essais :\n", "\n", "*Remarque :* on utilise ici un nouveau nom de fichier pour chaque cellule **essai_ecriture_V2.txt** puis **essai_ecriture_V3.txt** \n", "\n", "Commençons par un programme qui ne va pas fonctionner... :\n", " - on ouvre le fichier avec le mode 'w' \n", " - on écrit du texte dedans (méthode write())\n", " - puis on appelle la méthode readlines() pour relire le contenu " ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# ce programme va générer une erreur. Pourquoi ?\n", "fichier = open('data/essai_ecriture_V2.txt','w')\n", "fichier.write('Première ligne\\nDeuxième ligne')\n", "contenu = fichier.readlines()\n", "fichier.close()\n", "\n", "print(contenu)" ] }, { "source": [ "Le programme ci-dessous présente une petite modification qui va éviter cette erreur. Mais :\n", " - le contenu du fichier est-il affiché comme demandé ?\n", " - vérifier que le fichier **essai_ecriture_V3.txt** contient bien cependant le contenu écrit dedans" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# le programme corrigé : remarquer la modification dans la première ligne (relire le tableau donné au paragraphe 1)\n", "fichier = open('data/essai_ecriture_V3.txt','w+')\n", "fichier.write('Première ligne\\nDeuxième ligne')\n", "contenu = fichier.readlines()\n", "fichier.close()\n", "\n", "print(contenu)\n", "# ... mais le contenu du fichier est-il affiché ? " ] }, { "source": [ "#### 3.2.2 - Se positionner dans un fichier avec les méthodes tell() et seek()\n", "\n", "L’objet fichier est un objet itérable : pour se déplacer dedans, on utilise **un index**. Cet index n’est pas spécifié en Python lors de l’utilisation de toutes les méthodes vues jusqu’à présent.\n", "\n", "On dispose de deux autres méthodes :\n", " - La méthode **tell()** permet de connaître la valeur actuelle de l’index. Elle renvoie un entier. \n", " - la méthode **seek(offset, origine)** permet de placer l’index en un endroit précis du fichier à l'aide des paramètres **offset** et **origine** :\n", " - offset : décalage \n", " - origine : \n", " - valeur par défaut : 0 le décalage est réalisé à partir du début du fichier\n", " - 1 : le décalage est réalisé à partir de la valeur actuelle de l’index\n", " - 2 : le décalage est réalisé à partir de la fin du fichier\n", " " ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fichier = open('data/essai_ecriture_V4.txt','w+')\n", "fichier.write('Première ligne\\nDeuxième ligne')\n", "contenu = fichier.readllines()\n", "\n", "print('index actuel = ',fichier.tell())\n", "\n", "fichier.close()" ] }, { "source": [ "Observer la valeur obtenue pour cet index (31) avec le contenu du fichier observé avec un éditeur hexadécimal :\n", "\n", "![essai_ecriture_V4.png](images/essai_ecriture_V4.png)\n", "\n", "...l'index *pointe* à la fin du fichier après l'octet de valeur 0x65, donc il n'y a rien à lire !\n", "\n", "Pour corriger ce problème, **il faut remettre l'index à l'endroit désiré dans le fichier !**\n", "\n", "Remettons le au début (= on veut donc mettre le paramètre *offset* à 0) :" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fichier = open('data/essai_ecriture_V5.txt','w+')\n", "fichier.write('Première ligne\\nDeuxième ligne')\n", "\n", "fichier.seek(0) # on met ici offset à 0 ; le paramètre origine garde sa valeur par défaut (0)\n", " # cela signifie que l'on décale de 0 (offset) l'index par rapport à une origine 0\n", "print('index actuel = ',fichier.tell())\n", "contenu = fichier.read()\n", "print('index après lecture = ',fichier.tell(), '\\n')\n", "\n", "print('Contenu du fichier :')\n", "print(contenu)\n", "\n", "fichier.close()" ] }, { "source": [ "Modifier dans le code ci-dessus la valeur de l'offset tout en gardant la valeur par défaut du paramètre *origine* pour bien comprendre le fonctionemment de la méthode seek()." ], "cell_type": "markdown", "metadata": {} }, { "source": [ "### 3.3- Ecrire du texte dans un fichier existant\n", "\n", "On suppose que le fichier **essai_ecriture_V5.txt** existe dans le répertoire **data**, et qu’il est dans l’état fourni par le dernier programme exécuté ci-dessus.\n", "\n", "Dans la cellule ci-dessous, on utilise le **mode append (‘a’)** :\n", "\n", " - Quel en est l’effet :\n", "\n", " - Comment le programme précédent (celui qui créait les deux premières lignes de texte aurait du être modifié pour que l’aspect visuel du nouveau fichier créé soit correct (affichage de 4 lignes séparées) ?\n", "\n", "**A RETENIR : Quand on écrit dans un fichier, penser à la terminaison du fichier !... **\n", "\n", " - peut-on lire le fichier dans ce mode append ?\n", "\n", " - proposer une modification du programme\n", "\n", "**ATTENTION** : la méthode d'ouverture choisie *append* ajoute toujours les données à la fin du fichier (s'il existe déjà)... Si on exécute plusieurs fois la cellule, le contenu du fichier va devenir incohérent avec ce que l'on souhaite faire. Il faudra alors réexécuter la cellule précédente pour retrouver le fichier *essai_ecriture_V5.txt* dans son état d'origine" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nom_fichier = 'essai_ecriture_V5.txt'\n", "fichier = open('data/' + nom_fichier ,'a')\n", "fichier.write('Troisième ligne \\nQuatrième ligne')\n", "fichier.close()\n", "\n", "print('Ouvrir le fichier ',nom_fichier, 'dans un éditeur de texte pour vérifier le résultat obtenu')" ] }, { "source": [ "**Expérimenter et expérimenter encore car le travail sur les fichiers n’est pas si simple… et attention aux fichiers utilisés pour ces expérimentations car on a vite fait de les détruire sans le vouloir ! Donc travailler dans un dossierspécialement créé pour ces expérimentations...**\n" ], "cell_type": "markdown", "metadata": {} }, { "source": [ "## 4- Application : Retour sur la fable de La Fontaine :\n", "\n", "Ecrire un programme en Python qui prend un fichier **fable.txt**, le **laisse intact**, et crée un fichier **fable_num.txt** contenant le **même texte** mais **avec la numérotation des lignes** :\n", "\n", "![transformation_fable.png](images/transformation_fable.png)\n", "\n", "S'entraîner à faire différentes numérotations... On numérote :\n", " - toutes les lignes \n", " - seulement les lignes paires\n", " - la première ligne puis les lignes 5, 10, 15 \n", "\n", "*Coup de pouce :* il faut *ouvrir* deux fichiers :\n", " - un fichier *source* (la fable) que l'on va ouvrir pour en lire le contenu\n", " - un fichier de *destination* qui n'existe pas encore (fable_num.txt) et dans lequel on va écrire\n", "\n", "A la fin du travail sur ces fichiers, penser à appliquer la méthode **close()** dessus.\n", "\n" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# le programme à écrire :\n", "???\n", "...\n", "???" ] } ] }