La manipulation des fichiers texte ================================== L’objectif de ce chapitre est d’expliquer comment s’effectue, en langage Python, la lecture et l’écriture de données dans des fichiers. Seul le cas des fichiers de texte est abordé. Généralités sur l’utilisation de fichiers en programmation ---------------------------------------------------------- Pourquoi utiliser des fichiers ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Jusqu’à présent, les données que nous avons traitées dans des programmes étaient : - stockées dans des variables (simples ou structurées) grâce à des affectations, directement dans le code source - saisies au clavier puis stockées dans des variables au cours de l’exécution. Ces deux méthodes sont très limitées et inadaptées pour traiter de grandes quantités de données. Prenons l’exemple d’un programme calculant la moyenne obtenue par 1500 élèves à un contrôle de maths. Il n’est pas raisonnable de saisir au clavier les 1500 notes. En supposant qu’on puisse tout de même le faire, cela signifie que si le nombre de valeurs change il faudra modifier le code source du programme. Or, un programme bien conçu doit être indépendant des données qu’il traite. La quantité de données ainsi que leurs valeurs doivent pouvoir changer sans qu’on ait besoin de modifier le programme. Jusqu’à présent également, les résultats produits par nos programmes étaient simplement affichés à l’écran. Or, les résultats produits sont souvent trop volumineux pour être simplement affichés. Ils ont aussi parfois besoin d’être conservés, ce que ne permet pas l’affichage à l’écran. L’intérêt des fichiers est de bien séparer les programmes des données qu’ils traitent et des résultats qu’ils produisent. L’utilisation de fichiers permet de stocker dans des fichiers séparés : - le programme source - les données à traiter, c’est-à-dire les **entrées** ou le **fichier d’entrées** - les données produites, c’est-à-dire les **sorties** ou le **fichier de sorties**. Si on prend l’exemple d’un programme calculant les moyennes obtenues par des étudiants à trois examens (cf. figure ci-dessous), le fichier d’entrées contient la liste des étudiants, avec pour chacun les trois notes obtenues. Le fichier de sorties contient la même liste d’étudiants, avec pour chacun la moyenne obtenue. .. figure:: ImagesNotebook/EntreesSorties.PNG :alt: Programme utilisant des fichiers Programme utilisant des fichiers La seule contrainte imposée par l’utilisation de fichiers pour les entrées et les sorties est la définition précise et sans ambiguïté de la syntaxe (i.e., le format) de ces fichiers. Cette définition de la syntaxe doit précéder l’écriture du programme source, qui doit en tenir compte. Dans notre exemple, une syntaxe possible pour le fichier d’entrées est montrée sur la figure suivante. Dans cette syntaxe, chaque ligne contient les caractéristiques d’un étudiant(e) séparées par des virgules : nom, prénom, note obtenue au 1er examen, note obtenue au 2e examen, note obtenue au 3e examen. .. figure:: ImagesNotebook/SyntaxeEntrees.PNG :alt: Syntaxe du fichier d’entrées Syntaxe du fichier d’entrées Le fichier de sorties peut reprendre la même syntaxe que le fichier d’entrées, à ceci près que les 3 notes sont remplacées par la moyenne. .. figure:: ImagesNotebook/SyntaxeSorties.PNG :alt: Syntaxe du fichier de sorties Syntaxe du fichier de sorties Il existe principalement deux types de fichiers : - les fichiers texte - les fichiers binaires. Les fichiers texte ~~~~~~~~~~~~~~~~~~ Contenu ^^^^^^^ Comme son nom l’indique, un fichier texte contient des données textuelles, lisibles grâce à l’utilisation d’un **éditeur de texte** tel que **Notepad++**, **Bloc-notes**, **Xemacs** ou **Sublime Text**. Ces données textuelles peuvent être : - des caractères imprimables : lettres, chiffres, ponctuation, symboles, etc - des espaces - des caractères de fin de paragraphe ou de fin de ligne. Tous les programmes sources, quel que soit le langage utilisé, sont mémorisés dans des fichiers texte. Un fichier texte peut également être utilisé pour conserver des données textuelles brutes, c’est-à-dire sans aucune mise en forme. Notion d’encodage ^^^^^^^^^^^^^^^^^ Dans la mémoire vive d’un ordinateur ou sur un support de stockage tel qu’un disque dur, toutes les données, quels que soient leurs types, sont codées à l’aide d’un **langage binaire**, dont l’alphabet se compose de deux symboles uniquement : **0** et **1**. Chaque caractère d’un fichier texte est donc mémorisé à l’aide d’une **suite de bits**, c’est-à-dire une suite de chiffres dont la valeur est 0 ou 1. La suite de bits représentant un caractère dépend de l’\ **encodage** utilisé. En informatique, un encodage est un processus permettant de traduire un caractère en une suite plus ou moins longue de bits qui correspond à sa représentation binaire. Il existe de nombreux encodages, parmi lesquels nous pouvons citer les plus couramment utilisés : - ASCII : un des premiers encodages utilisés en informatique. Il n’est quasiment plus utilisé aujourd’hui car il ne permet l’encodage que de 128 caractères différents. - ISO-8859-1 ou ANSI : encodage dit « occidental » permettant de coder tous les caractères latins. - UTF-8 : encodage le plus répandu depuis 2016. Il permet de coder de nombreux caractères issus de nombreux alphabets (latin, arabe, cyrillique, etc). Le tableau suivant montre les représentations binaires des caractères « e » et « é » avec les trois encodages cités ci-dessus. ========= ========== ======== ======== Caractère ASCII ANSI UTF-8 ========= ========== ======== ======== e 01100101 01100101 01100101 é non défini 11101001 11000011 ========= ========== ======== ======== Nous pouvons remarquer que la représentation binaire de « e » est identique dans les trois encodages. En revanche, le « é » ne peut être codé en ASCII. De plus, sa représentation binaire est différente en ANSI et en UTF-8. Ces observations sont identiques pour tous les caractères accentués. Lorsqu’on travaille avec un fichier texte, il est primordial de savoir avec quel encodage il a été créé. Considérons par exemple un fichier texte utilisant l’encodage ANSI. Le contenu du fichier est le suivant : .. figure:: ImagesNotebook/Fichier_ANSI_Init.PNG :alt: Fichier texte initial Fichier texte initial Nous envoyons via la messagerie électronique ce fichier à deux amis A et B. Notre ami A récupère le fichier, puis l’ouvre dans son éditeur de texte en utilisant également l’encodage ANSI. Voici ce qui s’affiche sur l’écran de A : .. figure:: ImagesNotebook/Fichier_ANSI_Init.PNG :alt: Fichier ouvert avec ANSI Fichier ouvert avec ANSI Dans ce cas, tout se passe bien : l’encodage utilisé par A pour lire le fichier est le même que celui avec lequel nous l’avons créé. Notre ami B récupère également le fichier, mais l’ouvre dans son éditeur en utilisant l’encodage UTF-8. Voici ce qui s’affiche sur l’écran de B : .. figure:: ImagesNotebook/Fichier_UTF-8.PNG :alt: Fichier ouvert avec UTF-8 Fichier ouvert avec UTF-8 Dans ce cas, tous les caractères apparaissent normalement, sauf les caractères accentués ! Ce qui est normal puisque ces caractères ne sont pas codés de la même façon en ANSI et en UTF-8. Leur lecture avec un encodage UTF-8, alors qu’ils ont été mémorisés avec ANSI, ne peut donc pas s’effectuer correctement. Autre exemple illustrant l’importance de l’encodage pour le traitement des fichiers texte : voici ci-dessous un fichier texte encodé en UTF-8 : .. figure:: ImagesNotebook/Exemple2_utf-8.PNG :alt: Fichier encodé avec UTF-8 Fichier encodé avec UTF-8 Voici ci-dessous le même fichier ouvert avec un encodage ANSI : .. figure:: ImagesNotebook/Exemple2_ansi.PNG :alt: Fichier lu avec ANSI Fichier lu avec ANSI Tous les caractères accentués ont été mal décodés et apparaissent comme des suites de deux caractères telles que « é » (à la place de « é ») ou « ï » (à la place de « ï »). L’encodage est donc une information essentielle lorsqu’on traite des fichiers texte. Dans la plupart des langages de programmation, l’encodage est une information qui doit être fournie aux différentes instructions/fonctions/méthodes qui lisent les données contenues dans les fichiers texte. Tous les éditeurs permettent généralement de paramétrer l’encodage utilisé pour créer un fichier, ou de modifier l’encodage d’un fichier existant. Lorsqu’on ne connaît pas l’encodage d’un fichier texte, une bonne méthode est d’ouvrir ce fichier avec un éditeur, et de tester plusieurs encodages jusqu’à obtenir celui avec lequel le contenu du fichier (en particulier les caractères accentués !) est lisible. Les fichiers binaires ~~~~~~~~~~~~~~~~~~~~~ Un **fichier binaire** est un fichier contenant des données non textuelles qui sont illisibles avec un éditeur de texte. Voici quelques exemples de fichiers binaires : - une image (format, JPEG, GIF, PNG, etc) - un son (format WAV, MP3, etc) - une vidéo (format AVI, MOV, MP4, etc) - un programme exécutable (fichier ayant l’extension .exe) - un document Word, Excel, Libre Office, etc. La plupart du temps (à l’exception des programmes exécutables), les fichiers binaires sont créés à l’aide de logiciels dédiés (Paint, Gimp, Word, Excel, etc). Seuls ces logiciels dédiés permettent d’en lire les contenus. Prenons l’exemple d’un fichier contenant une image au format PNG. Lorsqu’on ouvre ce fichier avec le logiciel de traitement d’images Irfanview, l’image s’affiche correctement : .. figure:: ImagesNotebook/png_irfanview.PNG :alt: Image lue avec Irfanview Image lue avec Irfanview Lorsqu’on essaie d’ouvrir le même fichier avec un éditeur de texte, voici ce qui s’affiche : .. figure:: ImagesNotebook/png_texte.PNG :alt: Image lue avec éditeur Image lue avec éditeur Voici ce que donne un diaporama Powerpoint… ouvert avec Powerpoint : .. figure:: ImagesNotebook/diapo_ppt.PNG :alt: Diaporama lu avec Powerpoint Diaporama lu avec Powerpoint et ouvert avec un éditeur de texte : .. figure:: ImagesNotebook/diapo_texte.PNG :alt: Diaporama lu avec éditeur Diaporama lu avec éditeur Manipulation des fichiers texte : quelles fonctionnalités ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dans ce chapitre, nous nous intéresserons à la manipulation des fichiers texte uniquement. Pour pouvoir traiter correctement les fichiers texte, en tant que fichiers d’entrées ou de sorties, les programmes doivent être en mesure : - d’ouvrir un fichier se trouvant sur un support de stockage - de lire tout le contenu d’un fichier - de créer un nouveau fichier - d’écrire des données dans un fichier - de modifier des données se trouvant dans un fichier - de fermer un fichier. La plupart des langages proposent toutes ces fonctionnalités. Manipulation des fichiers texte en Python ----------------------------------------- Comme vous pourrez le constater dans la suite de cette section, un fichier en Python est considéré comme un objet. Voici donc les principales fonctions et méthodes permettant de traiter des fichiers texte. Récupération du **dossier courant** : la fonction **getcwd()** du module **os** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Le **dossier courant** est le dossier dans lequel s’exécute un programme. Ce dossier peut varier d’un environnement de programmation à un autre. Dans PyCharm, lorsqu’on exécute un programme Python, le dossier courant est celui où est stocké le programme. Dans VSCode, le dossier courant est le dossier qui a été ouvert pour travailler (qui n’est pas forcément celui où est stocké le programme qu’on exécute !). La fonction **getcwd()** permet de connaître le dossier courant dans lequel s’exécute un programme. Elle retourne une chaîne contenant le chemin complet du dossier courant. .. code:: ipython3 import os courant = os.getcwd() print(f"Le dossier courant est : {courant}.") .. parsed-literal:: Le dossier courant est : C:\\Users\\martienne_e. Lorsqu’un programme traite des fichiers, **ceux-ci doivent impérativement être stockés dans le dossier courant**. Modification du dossier courant : la fonction **chdir()** du module **os** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Il est possible de modifier le dossier courant avec la fonction **chdir()**. La syntaxe de cette fonction est la suivante : .. code:: ipython3 os.chdir(chemin) où ``chemin`` est une chaîne contenant le chemin du nouveau dossier courant (dossier dans lequel doivent impérativement figurer les fichiers à traiter !!). Ce chemin peut-être : - **absolu** : il s’agit alors d’un chemin complet dont le point de départ est la racine de l’espace de stockage. Le chemin : ``M:\MesDonnées\L1\Python\TD1`` est un exemple de chemin absolu indiquant l’emplacement complet du dossier ``TD1`` sur le disque ``M``. - **relatif** : il s’agit alors d’un chemin dont le point de départ est le dossier courant. Le chemin : ``Info\Bureautique`` est un exemple de chemin relatif indiquant l’emplacement du dossier ``Bureautique`` à partir du dossier courant. Le programme suivant est un exemple d’utilisation de la fonction **chdir()** permettant de paramétrer le dossier ``'C:\Users\martienne_e\data'`` comme nouveau dossier courant. .. code:: ipython3 import os courant = os.getcwd() print(f"Le dossier courant est : {courant}") nouveau = courant + '\data' os.chdir(nouveau) courant = os.getcwd() print(f"Le nouveau dossier courant est : {courant}") .. parsed-literal:: Le dossier courant est : C:\\Users\\martienne_e Le nouveau dossier courant est : C:\\Users\\martienne_e\\data Ouverture d’un fichier : la fonction **open()** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pour traiter un fichier dans un programme, la première chose à faire est d’ouvrir le fichier (un fichier est comme un livre : il faut l’ouvrir avant de pouvoir le lire !). C’est la fonction **open()** qui permet d’ouvrir un fichier. Elle prend en paramètres : - une chaîne (ou une variable de type chaîne) contenant le nom complet du fichier à ouvrir - une chaîne (ou une variable de type chaîne) représentant le **mode d’ouverture** du fichier. Le **mode d’ouverture** d’un fichier va déterminer le type du fichier et la manière dont on va l’utiliser. Il est représenté par une chaîne pouvant contenir les caractères suivants : - ``'t'`` pour un fichier de type texte (type par défaut). - ``'b'`` pour un fichier de type binaire. - ``'r'`` pour ouvrir un fichier en lecture seule, ce qui signifie qu’il sera possible de lire les données qu’il contient mais pas d’y écrire des données. - ``'w'`` pour ouvrir un fichier en écriture. Dans ce mode, il sera uniquement possible d’écrire dans le fichier. Si le fichier existe déjà, tout son contenu sera remplacé par les nouvelles données écrites par le programme. Si le fichier n’existe pas, il sera créé. - ``'a'`` pour ouvrir un fichier en ajout. Dans ce mode, il sera uniquement possible d’écrire dans le fichier. Si le fichier existe déjà, les données écrites viendront s’ajouter au contenu du fichier. Si le fichier n’existe pas, il sera créé. - ``'x'`` pour ouvrir un fichier en écriture et en création exclusive. Dans ce mode, le fichier ne doit pas exister sinon une erreur se produit. Le programme va créer le fichier et écrire les données qu’il produit dedans. Voici deux exemples de modes d’ouverture pouvant être fournis en paramètres de la fonction **open()** : - ``'tr'`` : fichier texte à ouvrir en lecture seule - ``'bw'`` : fichier binaire à ouvrir en écriture. Tout interpréteur Python considère par défaut que l’encodage des fichiers texte est celui du système utilisé (exemple : cp1252 sous Windows). Pour ouvrir un fichier texte en utilisant un encodage différent de celui par défaut, il faut ajouter à la fonction **open()** un troisième paramètre optionnel, nommé ``encoding``, dont la valeur est une chaîne correspondant au nom de l’encodage à utiliser. La fonction retourne en résultat un **descripteur de fichier** (ou **objet fichier**). Ce descripteur peut être comparé à **un curseur pointant sur un caractère précis du fichier**, et dont la position va varier en fonction des différentes opérations qui seront effectuées sur le fichier (lectures et/ou écritures). Un descripteur est positionné : - au tout début du fichier (c’est-à-dire sur le premier caractère) lorsque le mode d’ouverture est ``r``, ``w`` ou ``x`` - à la fin du fichier (c’est-à-dire juste après le dernier caractère) lorsque le mode d’ouverture est ``a``. Voici des exemples d’utilisation de la fonction **open()** : .. code:: ipython3 fichtxt = open('data.txt', 'rt', encoding='UTF-8') print(f"Descripteur de fichier : {fichtxt}") .. parsed-literal:: Descripteur de fichier : <_io.TextIOWrapper name='data.txt' mode='rt' encoding='UTF-8'> La fonction ouvre en lecture seule le fichier texte ``data.txt`` situé dans le dossier courant. L’encodage utilisé est UTF-8. Elle retourne le descripteur du fichier dans la variable ``fichtxt``. .. code:: ipython3 fichdata = open('sample.txt', 'rt') print(f"Descripteur de fichier : {fichdata}") .. parsed-literal:: Descripteur de fichier : <_io.TextIOWrapper name='sample.txt' mode='rt' encoding='cp1252'> La fonction ouvre en lecture seule le fichier texte ``sample.txt`` situé dans le dossier courant. L’encodage utilisé est celui par défaut : cp1252 (Windows). Elle retourne le descripteur du fichier dans la variable ``fichdata``. Fermeture d’un fichier : la méthode **close()** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dans un programme, dès qu’un fichier n’est plus utilisé il doit être fermé (comme un livre !!). La méthode **close()** permet de fermer un fichier. Elle ne prend aucun paramètre et s’applique au descripteur du fichier à fermer (valeur de retour de la fonction **open()**). Voici les deux exemples précédents, dans lesquels ont été ajoutées les fermetures des deux fichiers. .. code:: ipython3 # Ouverture du fichier fichtxt = open('data.txt', 'rt', encoding='UTF-8') # Affichage de l'objet référençant le fichier print(f"Descripteur du fichier : {fichtxt}") # Fermeture du fichier fichtxt.close() .. parsed-literal:: Descripteur du fichier : <_io.TextIOWrapper name='data.txt' mode='rt' encoding='UTF-8'> .. code:: ipython3 # Ouverture du fichier fichdata = open('sample.txt', 'rt') # Affichage de l'objet référençant le fichier print(f"Descripteur du fichier : {fichdata}") # Fermeture du fichier fichdata.close() .. parsed-literal:: Descripteur du fichier : <_io.TextIOWrapper name='sample.txt' mode='rt' encoding='cp1252'> Un fichier ouvert dans un programme et non fermé avant la fin du programme risque d’être endommagé. Il faut donc bien penser à fermer un fichier dès qu’il n’est plus utilisé. L’instruction **with**, que nous aborderons plus loin dans ce chapitre, permet la fermeture implicite et donc l’utilisation sécurisée d’un fichier. Lecture intégrale d’un fichier : les méthodes **read()** et **readlines()** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ La méthode **read()** s’applique à un descripteur de fichier, et permet de lire tout son contenu **à partir de la position courante du descripteur**. Lorsqu’aucun paramètre ne lui est fourni, elle retourne une chaîne contenant tous les caractères contenus dans le fichier, à partir de la position du descripteur jusqu’à la fin du fichier. Dans ce cas, après l’exécution de **read()**, le descripteur est positionné à la fin du fichier. Voici un exemple de lecture complète et d’affichage du contenu d’un fichier nommé ``data.txt`` (`Cliquer ici pour visualiser le contenu du fichier data.txt `__). .. code:: ipython3 # Ouverture du fichier # Le descripteur fichtxt est positionné sur le premier caractère du fichier fichtxt = open('data.txt', 'rt', encoding='UTF-8') # Lecture de tous les caractères et stockage dans une chaîne contenu = fichtxt.read() # Fermeture du fichier fichtxt.close() # Affichage de la chaîne print(contenu) .. parsed-literal:: Souvent, pour s’amuser, les hommes d’équipage Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, Le navire glissant sur les gouffres amers. A peine les ont-ils déposés sur les planches, Que ces rois de l’azur, maladroits et honteux, Laissent piteusement leurs grandes ailes blanches Comme des avirons traîner à côté d’eux. Ce voyageur ailé, comme il est gauche et veule ! Lui, naguère si beau, qu’il est comique et laid ! L’un agace son bec avec un brûle-gueule, L’autre mime, en boitant, l’infirme qui volait ! Le Poète est semblable au prince des nuées Qui hante la tempête et se rit de l’archer ; Exilé sur le sol au milieu des huées, Ses ailes de géant l’empêchent de marcher. Charles Baudelaire Il est possible de fournir en paramètre de la fonction **read()** un nombre entier indiquant le nombre de caractères à lire (à partir de la position du descripteur). Dans ce cas, après l’exécution de **read()**, le descripteur est positionné sur le premier caractère non lu du fichier. L’exemple suivant montre comment lire uniquement les 15 premiers caractères d’un fichier. .. code:: ipython3 # Ouverture du fichier # Le descripteur fichtxt est positionné sur le premier caractère du fichier fichtxt = open('data.txt', 'rt', encoding='UTF-8') # Lecture des 15 premiers caractères et stockage dans une chaîne contenu = fichtxt.read(15) # Fermeture du fichier fichtxt.close() # Affichage de la chaîne print(contenu) .. parsed-literal:: Souvent, pour s Tout comme la méthode **read()**, la méthode **readlines()** permet de lire le contenu d’un fichier. Elle s’applique au descripteur d’un fichier, et retourne le contenu du fichier (toujour à partir de la position du descripteur !) sous la forme d’une liste de chaînes, où chaque chaîne correspond à une ligne du fichier (1re chaîne = 1re ligne, 2e chaîne = 2e ligne, etc). Il s’agit donc d’une méthode adaptée pour effectuer un traitement ligne par ligne du contenu d’un fichier. Lorsqu’aucun paramètre ne lui est fourni, elle retourne toutes les lignes du fichier situées entre la position courante du descripteur et la fin du fichier (après son exécution, le descripteur est positionné à la fin du fichier). Il est possible de stopper la lecture des lignes lorsqu’un nombre de caractères a été atteint. Il suffit pour cela de fournir ce nombre en paramètre de la fonction. Dans ce cas, la lecture des lignes s’arrête lorsque le nombre de caractères souhaité a été atteint (et le descripteur est positionné sur le premier caractère non lu du fichier). Le programme suivant lit toutes les lignes du fichier ``data.txt``, affiche la liste de chaînes obtenues, puis chaque ligne du fichier une par une. .. code:: ipython3 # Ouverture du fichier # Le descripteur fichtxt est positionné sur le premier caractère du fichier fichtxt = open('data.txt', 'rt', encoding='UTF-8') # Lecture des lignes et stockage dans une liste de chaînes lignes = fichtxt.readlines() # Fermeture du fichier fichtxt.close() # Affichage de la liste de chaînes print(f"Liste des chaînes lues dans le fichier : \n{lignes}\n") # Affichage des lignes une par une print("Affichage ligne par ligne :\n") for l in lignes: print(l, end="") .. parsed-literal:: Liste des chaînes lues dans le fichier : ['Souvent, pour s’amuser, les hommes d’équipage\n', 'Prennent des albatros, vastes oiseaux des mers,\n', 'Qui suivent, indolents compagnons de voyage,\n', 'Le navire glissant sur les gouffres amers.\n', '\n', 'A peine les ont-ils déposés sur les planches,\n', 'Que ces rois de l’azur, maladroits et honteux,\n', 'Laissent piteusement leurs grandes ailes blanches\n', 'Comme des avirons traîner à côté d’eux.\n', '\n', 'Ce voyageur ailé, comme il est gauche et veule !\n', 'Lui, naguère si beau, qu’il est comique et laid !\n', 'L’un agace son bec avec un brûle-gueule,\n', 'L’autre mime, en boitant, l’infirme qui volait !\n', '\n', 'Le Poète est semblable au prince des nuées\n', 'Qui hante la tempête et se rit de l’archer ;\n', 'Exilé sur le sol au milieu des huées,\n', 'Ses ailes de géant l’empêchent de marcher.\n', '\n', 'Charles Baudelaire'] Affichage ligne par ligne : Souvent, pour s’amuser, les hommes d’équipage Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, Le navire glissant sur les gouffres amers. A peine les ont-ils déposés sur les planches, Que ces rois de l’azur, maladroits et honteux, Laissent piteusement leurs grandes ailes blanches Comme des avirons traîner à côté d’eux. Ce voyageur ailé, comme il est gauche et veule ! Lui, naguère si beau, qu’il est comique et laid ! L’un agace son bec avec un brûle-gueule, L’autre mime, en boitant, l’infirme qui volait ! Le Poète est semblable au prince des nuées Qui hante la tempête et se rit de l’archer ; Exilé sur le sol au milieu des huées, Ses ailes de géant l’empêchent de marcher. Charles Baudelaire Dans la liste de chaînes retournée par **readlines()**, on peut remarquer que chaque chaîne se termine par le caractère spécial de retour à la ligne ``\n``. Le programme suivant lit les lignes du fichier ``data.txt`` jusqu’à ce qu’une limite de 100 caractères soit atteinte. Il affiche ensuite une par une les lignes lues. .. code:: ipython3 # Ouverture du fichier # Le descripteur fichtxt est positionné sur le premier caractère du fichier fichtxt = open('data.txt', 'rt', encoding='UTF-8') # Lecture des lignes et stockage dans une liste de chaînes, jusqu'au 100e caractère lignes = fichtxt.readlines(100) # Fermeture du fichier fichtxt.close() # Affichage des lignes une par une print("Affichage ligne par ligne :\n") for l in lignes: print(l, end="") .. parsed-literal:: Affichage ligne par ligne : Souvent, pour s’amuser, les hommes d’équipage Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, On remarque que seules les 3 premières lignes du fichier ont été lues, ce qui signifie que le 100e caractère doit se trouver quelque part sur la 3e ligne. Écriture dans un fichier : les méthodes **write()** et **writelines()** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ La méthode **write()** s’applique à un descripteur de fichier, prend en paramètre une chaîne, et permet d’écrire cette chaîne dans le fichier **à la position courante du descripteur**. Cela suppose donc que le fichier soit ouvert soit en écriture, soit en ajout, et que toutes les données à écrire dans le fichier soient converties au préalable en chaînes. Après l’exécution de **write()**, le descripteur est positionné immédiatement après le dernier caractère écrit. Ainsi, lorsque plusieurs appels à **write()** sont effectués successivement sur le même descripteur de fichier, les chaînes sont écrites les unes à la suite des autres dans le fichier. Le programme suivant copie le contenu du fichier ``data.txt`` dans un fichier ``data_copie.txt``. La méthode **read()** est utilisée pour lire le contenu du fichier ``data.txt``. .. code:: ipython3 # Ouverture du fichier data.txt en lecture seule # Le descripteur original est positionné sur le premier caractère du fichier original = open('data.txt', 'rt', encoding='UTF-8') # Lecture du contenu du fichier data.txt contenu = original.read() # Fermeture du fichier data.txt original.close() # Ouverture du fichier data_copie.txt en écriture # Le descripteur copie est positionné sur le premier caractère du fichier copie = open('data_copie.txt', 'wt', encoding='UTF-8') # Ecriture du contenu de data.txt dans data_copie.txt copie.write(contenu) # Fermeture du fichier data_copie.txt copie.close() Le programme suivant ajoute une chaîne à la fin du fichier ``data_copie.txt`` créé précédemment, puis affiche le contenu de ce fichier. .. code:: ipython3 # Ouverture du fichier data_copie.txt en ajout # Le descripteur fich est positionné à la fin du fichier (après le dernier caractère) fich = open('data_copie.txt', 'at', encoding='UTF-8') # Ecriture de la chaîne à la fin du fichier fich.write('\nNé le 9 avril 1821 à Paris, mort le 31 août 1867 à Paris.\n') # Ouverture du fichier data_copie.txt en lecture # Le descripteur fich est positionné au début du fichier fich = open('data_copie.txt', 'rt', encoding='UTF-8') # Lecture du contenu du fichier après ajout contenu = fich.read() # Affichage du contenu print(contenu) # Fermeture du fichier fich.close() .. parsed-literal:: Souvent, pour s’amuser, les hommes d’équipage Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, Le navire glissant sur les gouffres amers. A peine les ont-ils déposés sur les planches, Que ces rois de l’azur, maladroits et honteux, Laissent piteusement leurs grandes ailes blanches Comme des avirons traîner à côté d’eux. Ce voyageur ailé, comme il est gauche et veule ! Lui, naguère si beau, qu’il est comique et laid ! L’un agace son bec avec un brûle-gueule, L’autre mime, en boitant, l’infirme qui volait ! Le Poète est semblable au prince des nuées Qui hante la tempête et se rit de l’archer ; Exilé sur le sol au milieu des huées, Ses ailes de géant l’empêchent de marcher. Charles Baudelaire Né le 9 avril 1821 à Paris, mort le 31 août 1867 à Paris. La méthode **writelines()** s’applique à un descripteur de fichier, prend en paramètre une liste de chaînes, et permet d’écrire toutes les chaînes de la liste dans le fichier, les unes après les autres, à partir de la position courante du descripteur. Après son exécution, le descripteur est positionné immédiatement après le dernier caractère écrit. Le programme suivant ajoute deux chaînes à la fin du fichier ``data_copie.txt`` créé précédemment, puis affiche le contenu de ce fichier. .. code:: ipython3 # Liste contenant les deux chaînes à ajouter chajout = ['Père : Joseph-François Baudelaire\n', 'Mère : Caroline Aupick\n'] # Ouverture du fichier data_copie.txt en ajout # Le descripteur fich est positionné à la fin du fichier (après le dernier caractère) fich = open('data_copie.txt', 'at', encoding='UTF-8') # Ecriture des chaînes de la liste à la fin du fichier fich.writelines(chajout) # Ouverture du fichier data_copie.txt en lecture # Le descripteur fich est positionné au début du fichier fich = open('data_copie.txt', 'rt', encoding='UTF-8') # Lecture du contenu du fichier après ajout contenu = fich.read() # Affichage du contenu print(contenu) # Fermeture du fichier fich.close() .. parsed-literal:: Souvent, pour s’amuser, les hommes d’équipage Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, Le navire glissant sur les gouffres amers. A peine les ont-ils déposés sur les planches, Que ces rois de l’azur, maladroits et honteux, Laissent piteusement leurs grandes ailes blanches Comme des avirons traîner à côté d’eux. Ce voyageur ailé, comme il est gauche et veule ! Lui, naguère si beau, qu’il est comique et laid ! L’un agace son bec avec un brûle-gueule, L’autre mime, en boitant, l’infirme qui volait ! Le Poète est semblable au prince des nuées Qui hante la tempête et se rit de l’archer ; Exilé sur le sol au milieu des huées, Ses ailes de géant l’empêchent de marcher. Charles Baudelaire Né le 9 avril 1821 à Paris, mort le 31 août 1867 à Paris. Père : Joseph-François Baudelaire Mère : Caroline Aupick L’instruction **with** et la fermeture automatique de fichier ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nous avons évoqué précédemment l’importance de fermer les fichiers, dès que leurs utilisations sont terminées. Pour ne pas avoir à se soucier de la fermeture des fichiers (et accessoirement ne pas risquer de perdre les données qu’ils contiennent !), il est possible (voire même fortement recommandé !) d’utiliser l’instruction **with**. Sa syntaxe est la suivante : .. code:: ipython3 with open() as desc_fich: bloc où : - ``open()`` est un appel à la fonction du même nom permettant d’ouvrir un fichier - ``desc_fich`` est un descripteur de fichier, dont la valeur est celle retournée par l’appel à ``open()`` - ``bloc`` est un bloc d’instructions, contenant notamment des opérations sur le fichier de descripteur ``desc_fich``. L’intérêt de cette instruction **with** est qu’elle va fermer automatiquement le fichier de descripteur ``desc_fich``, dès que l’exécution du bloc d’instructions sera terminée, et même si cette terminaison est dûe à une erreur. Utiliser **with**, c’est donc avoir l’assurance que le fichier sera fermé quoi qu’il arrive. Le programme suivant lit le contenu intégral d’un fichier ``demo.txt`` puis l’affiche, en utilisant l’instruction **with** (`Cliquer ici pour visualiser le contenu du fichier demo.txt `__). .. code:: ipython3 # Ouverture du fichier demo.txt en lecture # Le descripteur fich est positionné au début du fichier with open('demo.txt', 'rt', encoding='UTF-8') as fich: contenu = fich.read() print(contenu) .. parsed-literal:: Ligne 1 Ligne 2 Ligne 3 Ligne 4 Ligne 5 Ligne 6 Ligne 7 Ligne 8 Ligne 9 Ligne 10 Il est possible d’imbriquer deux instructions **with** (ou plus !), notamment dans un programme manipulant plusieurs fichiers texte. Le programme suivant copie le contenu du fichier ``demo.txt`` dans le fichier ``demo_copie.txt``. Deux instructions **with** sont utilisées : - une première pour ouvrir le fichier ``demo.txt`` en lecture seule - une seconde, imbriquée dans la première, pour l’ouverture du fichier ``demo_copie.txt`` en écriture. .. code:: ipython3 # Ouverture du fichier demo.txt en lecture # Le descripteur fich est positionné au début du fichier with open('demo.txt', 'rt', encoding='UTF-8') as fich: # Ouverture du fichier demo_copie.txt en écriture # Le descripteur nouveau est positionné au début du fichier with open('demo_copie.txt', 'wt', encoding='UTF-8') as nouveau: contenu = fich.read() # Lecture du contenu de demo.txt nouveau.write(contenu) # Ecriture du même contenu dans demo_copie.txt Notons au passage que l’imbrication des instructions **with** aurait pu se faire « dans l’autre sens ». .. code:: ipython3 with open('demo_copie.txt', 'wt', encoding='UTF-8') as nouveau: with open('demo.txt', 'rt', encoding='UTF-8') as fich: contenu = fich.read() nouveau.write(contenu) Parcours d’un fichier ligne par ligne : la boucle **for** et la méthode **readline()** associée à une boucle **while** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ La boucle ``for`` permet d’effectuer un traitement ligne par ligne d’un fichier texte. Sa syntaxe est la suivante : .. code:: ipython3 for ligne in desc_fich: bloc où : - ``ligne`` est une variable de type chaîne - ``desc_fich`` est le descripteur du fichier à parcourir - ``bloc`` est un bloc d’instructions. La variable ``ligne`` va prendre pour valeurs successives, une par une, toutes les lignes du fichier (une ligne = une chaîne) **à partir de la position courante du descripteur**. Chaque ligne sera terminée par le caractère de retour à la ligne ``'\n'``. Pour chaque ligne du fichier, une exécution de ``bloc`` est réalisée. Le programme suivant effectue un parcours ligne par ligne du fichier ``data_copie.txt``. Sur chaque ligne, l’application de la méthode ``strip()`` permet de supprimer le caractère de retour à la ligne ``'\n'``. Puis, le nombre de caractères de la ligne est affiché à l’écran. .. code:: ipython3 # Ouverture du fichier data_copie.txt en lecture # Le descripteur fich est positionné au début du fichier with open('data_copie.txt', 'rt', encoding='UTF-8') as fich: # Parcours du fichier ligne par ligne, suppression du caractère de retour à la ligne \n et affichage du nombre de caractères de chaque ligne for ligne in fich: ligne = ligne.strip('\n') print(len(ligne)) .. parsed-literal:: 45 47 44 42 0 45 46 49 39 0 48 49 40 48 0 42 44 37 42 0 18 57 33 22 On remarque que certaines lignes comportent 0 caractère. Ce sont les lignes vides qui ne contiennent que le caractère de retour à la ligne ``'\n'`` (caractère que la méthode ``strip()`` permet de supprimer avant le calcul du nombre de caractères). La méthode **readline()** permet de lire une ligne dans un fichier (toujours à partir de la position courante du descripteur du fichier !!), et de positionner le descripteur sur le premier caractère de la ligne suivante. Elle retourne : - le contenu de la ligne lue (sous la forme d’une chaîne terminée par ``'\n'``), si la fin du fichier n’est pas encore atteinte - une chaîne vide si la fin de fichier est atteinte. Le programme suivant lit puis affiche la première ligne du fichier nommé ``demo.txt`` (après lui avoir retiré le caractère de fin de ligne). .. code:: ipython3 # Ouverture du fichier demo.txt en lecture # Le descripteur fich est positionné au début du fichier with open('demo.txt', 'rt', encoding='UTF-8') as fich: premligne = fich.readline() # Lecture de la première ligne premligne = premligne.strip('\n') # Suppression du caractère de fin de ligne print(f"Voici le contenu de la première ligne du fichier : {premligne}.") .. parsed-literal:: Voici le contenu de la première ligne du fichier : Ligne 1. Associée à une boucle **while**, la méthode **readline()** permet d’effectuer un parcours ligne par ligne d’un fichier, pour appliquer à chaque ligne le même traitement. Le programme ci-dessous parcourt toutes les lignes du fichier ``demo.txt`` en affichant pour chacune son contenu et son nombre de caractères. .. code:: ipython3 # Ouverture du fichier demo.txt en lecture # Le descripteur fich est positionné au début du fichier with open('demo.txt', 'rt', encoding='UTF-8') as fich: premligne = fich.readline() # Lecture de la première ligne while premligne: # Equivalent à while premligne != '': premligne = premligne.strip('\n') # Suppression du caractère de fin de ligne print(f"Contenu : {premligne}, Nombre de caractères : {len(premligne)}") premligne = fich.readline() # Lecture de la ligne suivante .. parsed-literal:: Contenu : Ligne 1, Nombre de caractères : 7 Contenu : Ligne 2, Nombre de caractères : 7 Contenu : Ligne 3, Nombre de caractères : 7 Contenu : Ligne 4, Nombre de caractères : 7 Contenu : Ligne 5, Nombre de caractères : 7 Contenu : Ligne 6, Nombre de caractères : 7 Contenu : Ligne 7, Nombre de caractères : 7 Contenu : Ligne 8, Nombre de caractères : 7 Contenu : Ligne 9, Nombre de caractères : 7 Contenu : Ligne 10, Nombre de caractères : 8 Positionnement d’un descripteur de fichier : la méthode **seek()** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nous avons vu précédemment que les opérations de lecture et d’écriture dans un fichier s’effectuent toujours à partir de la position courante du descripteur dans le fichier. Un descripteur de fichier se crée à l’aide de la fonction **open()**, qui le positionne soit au début du fichier (premier caractère) soit à la fin (après le dernier caractère) selon le mode d’ouverture choisi. Les différentes opérations de lecture et/ou d’écriture modifient ensuite la position du descripteur dans le fichier. La méthode **seek()** s’applique à un descripteur de fichier et prend en paramètre un nombre correspondant à la position d’un caractère (0 pour le premier, 1 pour le deuxième, 2 pour le troisième, etc). Elle ne retourne aucun résultat mais positionne le descripteur sur le caractère situé à la position fournie en paramètre. Le programme suivant lit puis affiche plusieurs fois le contenu du fichier ``data.txt`` : dans sa totalité, à partir du caractère de la position 15 puis à partir du caractère de la position 50. .. code:: ipython3 # Ouverture du fichier data.txt # Le descripteur fichtxt est positionné sur le premier caractère du fichier with open('data.txt', 'rt', encoding='UTF-8') as fichtxt: complet = fichtxt.read() # Lecture de la totalité du fichier print(f"Contenu complet du fichier :\n\n{complet}\n") # Affichage de la totalité du fichier fichtxt.seek(15) # Positionnement de fichtxt sur le caractère situé à la position 15 partiel = fichtxt.read() # Lecture du fichier (à partir de la position de fichtxt : position 15) print(f"Contenu partiel du fichier à partir de la position 15 :\n\n{partiel}\n") # Affichage fichtxt.seek(50) # Positionnement de fichtxt sur le caractère situé à la position 50 partiel = fichtxt.read() # Lecture du fichier (à partir de la position de fichtxt : position 50) print(f"Contenu partiel du fichier à partir de la position 50 :\n\n{partiel}") # Affichage .. parsed-literal:: Contenu complet du fichier : Souvent, pour s’amuser, les hommes d’équipage Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, Le navire glissant sur les gouffres amers. A peine les ont-ils déposés sur les planches, Que ces rois de l’azur, maladroits et honteux, Laissent piteusement leurs grandes ailes blanches Comme des avirons traîner à côté d’eux. Ce voyageur ailé, comme il est gauche et veule ! Lui, naguère si beau, qu’il est comique et laid ! L’un agace son bec avec un brûle-gueule, L’autre mime, en boitant, l’infirme qui volait ! Le Poète est semblable au prince des nuées Qui hante la tempête et se rit de l’archer ; Exilé sur le sol au milieu des huées, Ses ailes de géant l’empêchent de marcher. Charles Baudelaire Contenu partiel du fichier à partir de la position 15 : ’amuser, les hommes d’équipage Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, Le navire glissant sur les gouffres amers. A peine les ont-ils déposés sur les planches, Que ces rois de l’azur, maladroits et honteux, Laissent piteusement leurs grandes ailes blanches Comme des avirons traîner à côté d’eux. Ce voyageur ailé, comme il est gauche et veule ! Lui, naguère si beau, qu’il est comique et laid ! L’un agace son bec avec un brûle-gueule, L’autre mime, en boitant, l’infirme qui volait ! Le Poète est semblable au prince des nuées Qui hante la tempête et se rit de l’archer ; Exilé sur le sol au milieu des huées, Ses ailes de géant l’empêchent de marcher. Charles Baudelaire Contenu partiel du fichier à partir de la position 50 : Prennent des albatros, vastes oiseaux des mers, Qui suivent, indolents compagnons de voyage, Le navire glissant sur les gouffres amers. A peine les ont-ils déposés sur les planches, Que ces rois de l’azur, maladroits et honteux, Laissent piteusement leurs grandes ailes blanches Comme des avirons traîner à côté d’eux. Ce voyageur ailé, comme il est gauche et veule ! Lui, naguère si beau, qu’il est comique et laid ! L’un agace son bec avec un brûle-gueule, L’autre mime, en boitant, l’infirme qui volait ! Le Poète est semblable au prince des nuées Qui hante la tempête et se rit de l’archer ; Exilé sur le sol au milieu des huées, Ses ailes de géant l’empêchent de marcher. Charles Baudelaire Utilisation des fichiers au format CSV en Python : le module csv ---------------------------------------------------------------- Dans cette section, nous présentons les principales fonctions du module CSV de Python, permettant le traitement des fichiers texte au format CSV. Qu’est-ce que le format CSV ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **CSV** signifie **Comma-Separated Values**, autrement dit **valeurs séparées par des virgules**. Il s’agit d’un format de données textuelles permettant de stocker des données **tabulaires**, c’est-à-dire des données qui pourraient être représentées dans un tableau. Il est très utilisé dans les systèmes de gestion de bases de données, ainsi que dans les logiciels tableurs, pour faciliter l’échange de données d’un logiciel à l’autre. Dans un fichier au format CSV : - chaque ligne correspond à une ligne d’un tableau, la première ligne pouvant contenir les intitulés des différents colonnes (cette première ligne d’intitulés n’est pas obligatoire) - sur chaque ligne, les contenus des différentes colonnes sont séparés à l’aide d’un **caractère séparateur** - certaines colonnes peuvent avoir un contenu vide. Voici ci-dessous un exemple de fichier au format CSV. La première ligne est la ligne d’intitulés (7 intitulés, donc 7 colonnes). Le caractère séparateur est la virgule « , ». Aucune des lignes ne comporte de colonne vide. .. figure:: ImagesNotebook/Exemple_Fichier_CSV.PNG :alt: Fichier au format CSV Fichier au format CSV Dialecte d’un fichier CSV ~~~~~~~~~~~~~~~~~~~~~~~~~ Le dialecte d’un fichier CSV désigne l’ensemble des règles utilisées pour formater les données qu’il contient. Le format CSV n’étant pas normalisé, il existe de nombreux dialectes différents. Dans l’exemple ci-dessous, le dialecte du fichier se compose entre autres des trois règles de formatage suivantes : - l’utilisation du point-virgule « ; » comme caractère séparateur des colonnes - les guillemets doubles anglo-saxons « “” » autour des données textuelles (pour les distinguer des données numériques) - l’utilisation du point « . » comme séparateur décimal des données numériques. .. image:: ImagesNotebook/dialecte_CSV.PNG Les fonctions **has_header()** et **sniff()** fournies par le module CSV prennent en paramètre un extrait du contenu d’un fichier au format CSV, puis analysent cet extrait. La fonction **has_reader()** retourne un booléen dont la valeur est ``True`` si la première ligne du fichier est bien une ligne d’intitulés, et ``False`` sinon. La fonction **sniff()** retourne un **objet dialecte** contenant toutes les règles de formatage du fichier, résumées dans des variables telles que : - ``lineterminator`` : le caractère de fin de ligne - ``delimiter`` : le caractère séparateur - ``quotechar`` : le caractère utilisé pour entourer les données susceptibles de contenir le caractère séparateur - etc Le programme ci-dessous utilise ces deux fonctions pour analyser un fichier nommé ``donnees.csv`` puis afficher le résultat de son analyse (`Cliquer ici pour visualiser le contenu du fichier donnees.csv `__). Notons que le fichier à analyser doit être préalablement ouvert en lecture. .. code:: ipython3 import os, csv with open('donnees.csv', 'rt', encoding='UTF-8') as f: extrait = f.read() intitules = csv.Sniffer().has_header(extrait) # Existence ou non d'une ligne d'intitulés dialect = csv.Sniffer().sniff(extrait) # Analyse du dialecte utilisé pour le formatage if intitules: print("Le fichier analysé possède une ligne d'intitulés.") else: print("Le fichier analysé ne possède pas de ligne d'intitulés.") print("Voici quelques propriétés du dialecte utilisé :") print(f"Caractère séparateur : {dialect.delimiter}") print(f"Caractère entourant les données pouvant contenir le caractère séparateur : {dialect.quotechar}") .. parsed-literal:: Le fichier analysé possède une ligne d'intitulés. Voici quelques propriétés du dialecte utilisé : Caractère séparateur : ; Caractère entourant les données pouvant contenir le caractère séparateur : " Les résultats du programme sont bien conformes aux caractéristiques du fichier ``donnees.csv`` dont voici ci-dessous un extrait. .. figure:: ImagesNotebook/Fichier_Donnees_csv.PNG :alt: Extrait du fichier donnees.csv Extrait du fichier donnees.csv La première ligne est bien une ligne d’intitulés. Le caractère séparateur est bien le point-virgule « ; ». De plus, les données textuelles susceptibles de contenir le caractère séparateur sont entourés du caractère « ” » (caractère nommé ``quotechar``). Lecture d’un fichier CSV ~~~~~~~~~~~~~~~~~~~~~~~~ Pour lire les données textuelles contenues dans un fichier CSV, il faut tout d’abord créer un **lecteur** (un ``reader``) de ce fichier, c’est-à-dire un objet capable de décoder le fichier. La lecture des données s’effectue ensuite à partir de ce lecteur. Création d’un **lecteur** : la fonction **csv.reader()** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ La fonction **csv.reader()** prend en paramètres : - le descripteur du fichier CSV à lire, qui doit être ouvert en lecture - un objet dialecte contenant le dialecte du fichier CSV et retourne le lecteur associé au fichier CSV. Le dialecte du fichier CSV à lire peut être obtenu en utilisant la méthode **sniff()** vue précédemment. Le programme suivant ouvre le fichier ``donnees.csv``, récupère son dialecte, re-positionne le descripteur au début du fichier (l’instruction ``f.read()`` a eu pour effet de le déplacer en fin de fichier) puis crée un lecteur pour le fichier. Notons que la variable ``dialect`` contenant le dialecte (valeur de retour de la méthode **sniff()**) est passée en paramètre de la fonction **csv.reader()**. .. code:: ipython3 import os, csv # Ouverture du fichier donnees.csv # Le descripteur f est positionné sur le premier caractère du fichier with open('donnees.csv', 'rt', encoding='UTF-8') as f: dialect = csv.Sniffer().sniff(f.read()) # Récupération du dialecte. Après f.read(), le descripteur f est positionné à la fin du fichier f.seek(0) # Re-positionnement de f au début du fichier (indispensable avant la création du lecteur) lecteur = csv.reader(f, dialect) # Création du lecteur Lecture des données à partir du lecteur ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Un lecteur de fichier (résultat de la fonction **csv.reader()**) est un objet itérable. Il peut donc être utilisé pour parcourir ligne par ligne les données se trouvant dans le fichier. Ce parcours s’effectue à l’aide d’une boucle ``for`` dont la syntaxe est la suivante : .. code:: ipython3 for ligne in lect: bloc où : - ``ligne`` est une variable - ``lect`` est lecteur - ``bloc`` est un bloc d’instructions. La variable ``ligne`` va prendre successivement pour valeurs toutes les lignes du fichier, de la première à la dernière. Pour chaque ligne, une exécution de ``bloc`` est effectuée. Chaque ligne placée dans la variable ``ligne`` est une liste de chaînes, où la première chaîne correspond à la première valeur de la ligne, la deuxième chaîne à la deuxième valeur, etc. On accède à chaque valeur de la ligne à l’aide de sa position dans la liste. A la fin d’une itération sur un lecteur, le descripteur du fichier se situe à la fin du fichier. Pour pouvoir refaire une itération sur le lecteur, il faut re-positionner le descripteur au début du fichier avec la méthode **seek()**. Le programme suivant effectue deux parcours du contenu du fichier ``donnees.csv`` ligne par ligne, à l’aide de son lecteur et d’une boucle ``for``. Lors du premier parcours, chaque ligne est affichée en séparant les différentes valeurs de la ligne par un tiret horizontal. Lors du second parcours, chaque ligne est affichée en séparant les différentes valeurs de la ligne par un trait vertical. .. code:: ipython3 import os, csv # Ouverture du fichier donnees.csv # Le descripteur f est positionné sur le premier caractère du fichier with open('donnees.csv', 'rt', encoding='UTF-8') as f: dialect = csv.Sniffer().sniff(f.read()) # Récupération du dialecte. Après f.read(), le descripteur f est positionné à la fin du fichier f.seek(0) # Re-positionnement de f au début du fichier (indispensable avant la création du lecteur) lecteur = csv.reader(f, dialect) # Création du lecteur print("Premier parcours : ") for ligne in lecteur: # Premier parcours ligne par ligne du fichier à l'aide du lecteur print(f"{ligne[0]} - {ligne[1]} - {ligne[2]} - {ligne[3]} - {ligne[4]} - {ligne[5]} - {ligne[6]}") f.seek(0) # Re-positionnement de f au début du fichier (indispensable avant le second parcours) print("\nSecond parcours : ") for ligne in lecteur: print(f"{ligne[0]} | {ligne[1]} | {ligne[2]} | {ligne[3]} | {ligne[4]} | {ligne[5]} | {ligne[6]}") .. parsed-literal:: Premier parcours : Numéro - Nom - Nbinscrits - Nbvotants - Nbblancs - Nbnuls - Nbexp 44 - Grand Est - 3864055 - 2847382 - 158769 - 54418 - 2634195 75 - Nouvelle-Aquitaine - 4462866 - 3383501 - 250345 - 103875 - 3029281 84 - Auvergne-Rhône-Alpes - 5558964 - 4136309 - 277883 - 91317 - 3767109 27 - Bourgogne-Franche-Comté - 1992557 - 1491021 - 102236 - 38647 - 1350138 53 - Bretagne - 2562764 - 1996495 - 142782 - 48102 - 1805611 24 - Centre-Val de Loire - 1837674 - 1373416 - 89671 - 33483 - 1250262 11 - Île-de-France - 7353133 - 5192986 - 320171 - 86980 - 4785835 76 - Occitanie - 4324603 - 3251774 - 238975 - 99581 - 2913218 32 - Hauts-de-France - 4256336 - 3119882 - 160771 - 68989 - 2890122 28 - Normandie - 2414345 - 1830658 - 116648 - 36854 - 1677156 52 - Pays de la Loire - 2833848 - 2164728 - 144187 - 46252 - 1974289 93 - Provence-Alpes-Côte d'Azur - 3664768 - 2656700 - 155435 - 49262 - 2452003 94 - Corse - 243000 - 147902 - 9534 - 4552 - 133816 01 - Guadeloupe - 315941 - 149057 - 8511 - 8211 - 132335 02 - Martinique - 304670 - 138469 - 11198 - 7353 - 119918 03 - Guyane - 103058 - 40088 - 3049 - 1768 - 35271 04 - La Réunion - 676080 - 401492 - 20584 - 16644 - 364264 06 - Mayotte - 92421 - 42044 - 1645 - 1997 - 38402 Second parcours : Numéro | Nom | Nbinscrits | Nbvotants | Nbblancs | Nbnuls | Nbexp 44 | Grand Est | 3864055 | 2847382 | 158769 | 54418 | 2634195 75 | Nouvelle-Aquitaine | 4462866 | 3383501 | 250345 | 103875 | 3029281 84 | Auvergne-Rhône-Alpes | 5558964 | 4136309 | 277883 | 91317 | 3767109 27 | Bourgogne-Franche-Comté | 1992557 | 1491021 | 102236 | 38647 | 1350138 53 | Bretagne | 2562764 | 1996495 | 142782 | 48102 | 1805611 24 | Centre-Val de Loire | 1837674 | 1373416 | 89671 | 33483 | 1250262 11 | Île-de-France | 7353133 | 5192986 | 320171 | 86980 | 4785835 76 | Occitanie | 4324603 | 3251774 | 238975 | 99581 | 2913218 32 | Hauts-de-France | 4256336 | 3119882 | 160771 | 68989 | 2890122 28 | Normandie | 2414345 | 1830658 | 116648 | 36854 | 1677156 52 | Pays de la Loire | 2833848 | 2164728 | 144187 | 46252 | 1974289 93 | Provence-Alpes-Côte d'Azur | 3664768 | 2656700 | 155435 | 49262 | 2452003 94 | Corse | 243000 | 147902 | 9534 | 4552 | 133816 01 | Guadeloupe | 315941 | 149057 | 8511 | 8211 | 132335 02 | Martinique | 304670 | 138469 | 11198 | 7353 | 119918 03 | Guyane | 103058 | 40088 | 3049 | 1768 | 35271 04 | La Réunion | 676080 | 401492 | 20584 | 16644 | 364264 06 | Mayotte | 92421 | 42044 | 1645 | 1997 | 38402 La lecture du contenu d’un fichier à partir de son lecteur peut également se faire en transformant le lecteur en une liste de listes, à l’aide de la fonction ``list``. La structure de la liste de listes obtenue est la suivante : .. figure:: ImagesNotebook/Struct_Liste_Lecteur.PNG :alt: Structure de la liste de listes du lecteur Structure de la liste de listes du lecteur Chaque liste de la liste est une liste de chaînes, où la première chaîne est la première valeur de la liste (colonne 1), la deuxième chaîne est la deuxième valeur (colonne 2), etc. L’intérêt de cette méthode est qu’elle permet de mémoriser tout le contenu du fichier dans une liste. Le fichier peut donc être fermé une fois ses données lues, comme le montre le programme ci-dessous. .. code:: ipython3 import os, csv # Ouverture du fichier donnees.csv # Le descripteur f est positionné sur le premier caractère du fichier with open('donnees.csv', 'rt', encoding='UTF-8') as f: dialect = csv.Sniffer().sniff(f.read()) # Récupération du dialecte. Après f.read(), le descripteur f est positionné à la fin du fichier f.seek(0) # Re-positionnement de f au début du fichier (indispensable avant la création du lecteur) lecteur = csv.reader(f, dialect) # Création du lecteur contenu = list(lecteur) # Transformation du lecteur en une liste de listes mémorisée dans une variable print(f"Le contenu du fichier est : \n{contenu}") # Affichage du contenu du fichier (après la fermeture du fichier) .. parsed-literal:: Le contenu du fichier est : [['Numéro', 'Nom', 'Nbinscrits', 'Nbvotants', 'Nbblancs', 'Nbnuls', 'Nbexp'], ['44', 'Grand Est', '3864055', '2847382', '158769', '54418', '2634195'], ['75', 'Nouvelle-Aquitaine', '4462866', '3383501', '250345', '103875', '3029281'], ['84', 'Auvergne-Rhône-Alpes', '5558964', '4136309', '277883', '91317', '3767109'], ['27', 'Bourgogne-Franche-Comté', '1992557', '1491021', '102236', '38647', '1350138'], ['53', 'Bretagne', '2562764', '1996495', '142782', '48102', '1805611'], ['24', 'Centre-Val de Loire', '1837674', '1373416', '89671', '33483', '1250262'], ['11', 'Île-de-France', '7353133', '5192986', '320171', '86980', '4785835'], ['76', 'Occitanie', '4324603', '3251774', '238975', '99581', '2913218'], ['32', 'Hauts-de-France', '4256336', '3119882', '160771', '68989', '2890122'], ['28', 'Normandie', '2414345', '1830658', '116648', '36854', '1677156'], ['52', 'Pays de la Loire', '2833848', '2164728', '144187', '46252', '1974289'], ['93', "Provence-Alpes-Côte d'Azur", '3664768', '2656700', '155435', '49262', '2452003'], ['94', 'Corse', '243000', '147902', '9534', '4552', '133816'], ['01', 'Guadeloupe', '315941', '149057', '8511', '8211', '132335'], ['02', 'Martinique', '304670', '138469', '11198', '7353', '119918'], ['03', 'Guyane', '103058', '40088', '3049', '1768', '35271'], ['04', 'La Réunion', '676080', '401492', '20584', '16644', '364264'], ['06', 'Mayotte', '92421', '42044', '1645', '1997', '38402']] Écriture dans un fichier CSV ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pour écrire des données textuelles dans un fichier CSV, il faut tout d’abord créer un **écrivain** (un ``writer``) de ce fichier, c’est-à-dire un objet capable d’écrire dans le fichier. L’écriture des données s’effectue ensuite à partir de cet écrivain. Création d’un **écrivain** : la fonction **csv.writer()** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ La fonction **csv.writer()** prend en paramètres : - le descripteur du fichier CSV dans lequel les données doivent être écrites, qui doit être ouvert en écriture - des paramètres facultatifs précisant certains éléments du dialecte (comme par exemple la valeur du caractère séparateur (``delimiter``) ou celle du caractère de fin de ligne (``lineseparator``) et retourne l’écrivain associé au fichier CSV. Le programme suivant ouvre le fichier ``resultats.csv`` en écriture, puis crée un écrivain pour ce fichier. Le caractère séparateur est le point-virgule et le caractère de fin de ligne ``'\n'``. .. code:: ipython3 import os, csv with open('resultats.csv', 'wt', encoding='UTF-8') as f: ecrivain = csv.writer(f, delimiter=";", lineterminator = "\n") Écriture des données à l’aide de l’écrivain : la méthode **writerow()** ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ La méthode **writerow()** s’applique à un écrivain et permet d’écrire une nouvelle ligne dans le fichier associé, à la position courante du descripteur. Elle prend comme unique paramètre un tuple ou une liste de chaînes dont les éléments sont les différentes valeurs de la ligne. Le tuple ou la liste peut être défini(e) à l’aide de la compréhension de liste (de tuple). Le programme suivant écrit deux lignes à la fin du fichier ``resultats.csv``. .. code:: ipython3 import os, csv # Ouverture du fichier resultats.csv en écriture # Le descripteur f est positionné à la fin du fichier with open('resultats.csv', 'wt', encoding='UTF-8') as f: ecrivain = csv.writer(f, delimiter=";", lineterminator = "\n") ecrivain.writerow(['Nom', 'Prénom', 'Age']) ecrivain.writerow(('Dutilleul', 'Armand', '28')) Voici ci-dessous le contenu du fichier ``resultats.csv`` après exécution du programme. .. figure:: ImagesNotebook/Contenu_Resultats_csv.PNG :alt: Contenu du fichier resultats.csv Contenu du fichier resultats.csv Exemple complet ~~~~~~~~~~~~~~~ Dans cette section, nous présentons un exemple de programme traitant des données mémorisées dans un fichier au format CSV. Le fichier de départ, nommé ``notes.csv``, contient sur chaque ligne le nom, le prénom d’un(e) étudiant(e) ainsi que trois notes obtenues à des examens (`Cliquer ici pour visualiser le contenu du fichier notes.csv `__). .. figure:: ImagesNotebook/Contenu_Donnees_csv.PNG :alt: Contenu du fichier notes.csv Contenu du fichier notes.csv Le programme parcourt ligne par ligne le fichier ``notes.csv`` afin de calculer la moyenne des trois notes de chaque étudiant(e). Il crée un nouveau fichier au format CSV, nommé ``moyennes.csv``, dont le contenu est identique à celui de ``notes.csv`` à ceci près que les trois notes d’examens sont remplacées par les moyennes des trois notes. .. code:: ipython3 import os, csv with open('notes.csv', 'rt', encoding='UTF-8') as fnotes: # Ouverture du fichier notes.csv en lecture seule dialect = csv.Sniffer().sniff(fnotes.read()) # Récupération du dialecte fnotes.seek(0) # Re-positionnement du descripteur au début du fichier lecteur = csv.reader(fnotes, dialect) # Création du lecteur du fichier notes.csv with open('moyennes.csv', 'wt', encoding='UTF-8') as fmoy: # Ouverture du fichier moyennes.csv en écriture ecrivain = csv.writer(fmoy, delimiter=";",lineterminator = "\n") # Création d'un écrivain du fichier moyennes.csv for etudiant in lecteur: # Parcours ligne par ligne du fichier notes.csv à l'aide de son lecteur nom = etudiant[0] # Stockage dans des variables des informations sur l'étudiant(e) prenom = etudiant[1] note1 = float(etudiant[2]) # Les 3 notes lues sont des chaînes et doivent être converties en float note2 = float(etudiant[3]) note3 = float(etudiant[4]) moyenne = (note1+note2+note3)/3 # Calcul de la moyenne de l'étudiant(e) ecrivain.writerow([nom, prenom, str(moyenne)]) # Ecriture des informations sur l'étudiant(e) dans le fichier moyennes.csv à l'aide de l'écrivain Le contenu du fichier ``moyennes.csv`` obtenu est le suivant : .. figure:: ImagesNotebook/Contenu_Moyennes_csv.PNG :alt: Contenu du fichier moyennes.csv Contenu du fichier moyennes.csv