.. _prog-e3-files: ************ Les fichiers ************ Les fichiers sont une abstraction fondamentale en informatique. Ils permettent de stocker des données de manière persistante, de les partager entre programmes, et d'interagir avec le système de fichiers. Comprendre les fichiers est essentiel pour tout programmeur, car ils sont omniprésents dans la plupart des applications. En première approche, un fichier est simplement une séquence persistante d'informations identifiée par un nom. Les idées importantes ici sont : * les données survivent à l'arrêt du programme (persistance) ; * les données sont stockées quelque part (disque, SSD, cloud, clé USB, etc.) de façon transparente pour le programme ; * on y accède à travers un identifiant (nom, chemin, URL). Ainsi, un fichier texte, une base de données, un objet dans un stockage cloud, une réponse HTTP, ou un flux Kafka sont vus comme des variantes d'une même idée : une source de données persistantes manipulée par des opérations de lecture et d'écriture. Les opérations abstraites ========================= Un programme interagit avec un fichier par quelques opérations universelles. ============ ========================================= Opération Signification ============ ========================================= ouvrir obtenir l'accès au contenu lire récupérer des données écrire ajouter/modifier des données position récupérer la position courante se déplacer changer la position de lecture/écriture fermer libérer l'accès ============ ========================================= On peut considérer qu'**un fichier est une source ou une destination de flux de données.** et que le programme possède à tout moment un curseur qui pointe sur la position courante:: A B C D E F G ^ curseur Lors d'une lecture le curseur avance et que l'opération **lire un caractère** retourne le caractère lu et déplace le curseur:: A B C D E F G ^ Cette idée est commune à tous les langages de programmation et à tous les types de fichiers (texte, binaire, etc.). Elle permet de modéliser des opérations plus complexes comme : * lire une ligne (jusqu'à un saut de ligne) ; * lire un bloc de données (par exemple 1024 octets) ; * lire jusqu'à la fin du fichier ; * etc. .. quiz:: quiz-abstract-operations :title: Les opérations abstraites - :quiz:`{"type":"TF","answer":"T"}` Un fichier est une séquence persistante de données identifiée par un nom. - Le programme possède à tout moment un :quiz:`{"type":"FB","answer":"curseur","flags":"fuzzy", "size":"8"}` qui pointe sur la position courante du fichier. - :quiz:`{"type":"TF","answer":"T"}` Les données dans un fichier survivent à l'arrêt du programme. - L'opération :quiz:`{"type":"SC","values":"lecture,écriture,fermeture,curseur","answer":"écriture"}` modifie le contenu du fichier - Les deux opérations principales sur un fichier sont :quiz:`{"type":"FB","answer":"lire écrire","flags":"sequence"}` - :quiz:`{"type":"TF","answer":"F"}` Les opérations abstraites sur fichiers sont différentes selon le type de fichier (texte vs binaire). .. _prog-e3-c-files: Les fichiers en langage C ========================= En langage C, les fichiers sont manipulés à travers un ensemble de fonctions standard définies dans la bibliothèque ``stdio.h``. Les fichiers sont représentés par des pointeurs de type ``FILE*``, et les opérations de lecture et d'écriture sont effectuées à l'aide de fonctions telles que ``fopen``, ``fread``, ``fwrite``, ``fclose``, etc. Par exemple, le code suivant ouvre un fichier texte en lecture, lit son contenu et l'affiche à l'écran. .. code-block:: c #include int main() { FILE *file = fopen("example.txt", "r"); if (file == NULL) { perror("Erreur lors de l'ouverture du fichier"); return 1; } char buffer[256]; while (fgets(buffer, sizeof(buffer), file) != NULL) { printf("%s", buffer); } fclose(file); return 0; } .. quiz:: quiz-c-files :title: Les fichiers en langage C - Les fonctions de manipulation de fichiers en C sont définies dans la bibliothèque :quiz:`{"type":"FB","answer":"stdio.h","flags":"fuzzy"}` - Les fichiers en C sont représentés par des pointeurs de type :quiz:`{"type":"FB","answer":"FILE*","size":4}` - :quiz:`{"type":"SC","values":"fopen,fread,fwrite,fclose","answer":"fopen"}` ouvre un fichier en langage C - :quiz:`{"type":"TF","answer":"T"}` La fonction ``fopen`` retourne un pointeur de type ``FILE*``. - Les fonctions :quiz:`{"type":"FB","answer":"fread fwrite","flags":"sequence"}` permettent de lire et d'écrire des données binaires. - :quiz:`{"type":"TF","answer":"T"}` La fonction ``fclose`` permet de libérer les ressources associées à un fichier. .. _prog-e3-python-files: Les fichiers en Python ====================== Une vidéo de présentation des fichiers... .. raw:: html Python peut traiter des fichiers texte et des fichiers binaires (images, sons, etc...). Pour ce cours, nous travaillerons exclusivement avec des fichiers contenant du texte. Nous utiliserons à titre d'exemple l'oeuvre de Corneille, Le Cid, contenue dans le fichier :download:`lecid.txt `. Ouverture du fichier -------------------- Ouvrir un fichier c'est établir la connexion avec la ressource contenant les données. On ouvre un fichier avec la fonction :func:`open` qui peut ne nécessiter qu'un seul argument, le nom du fichier à ouvrir. Si aucune arborescence n'est précisée, le répertoire dans lequel le fichier est recherché est celui depuis lequel a été lancé l'interpréteur Python. .. code-block:: python >>> f = open('lecid.txt') >>> f <_io.TextIOWrapper name='index.rst' mode='r' encoding='cp1252'> Le mode d'ouverture par défaut du fichier est en lecture seule : ``mode='r'`` et l'encodage est celui du terminal depuis lequel a été lancé l'interpréteur Python : ``encoding='cp1252'``. Ce sera cependant une bonne pratique que de préciser explicitement ces deux paramètres. :func:`open` retourne un `file-object `_ de type :class:`io.TextIOWrapper`. Cette classe hérite de :class:`io.TextIOBase` et :class:`io.IOBase`. .. code-block:: python >>> type(f) >>> import io >>> isinstance(f, io.TextIOBase) True >>> isinstance(f, io.IOBase) True L'objet retourné par :func:`open` dispose de plusieurs méthodes. Ici seules les plus importantes ont été conservées pour l'affichage. L'encodage utilisé est également disponible dans un attribut. .. code-block:: python >>> dir(f) [..., 'close', ..., 'read', ..., 'readline', 'readlines', ..., 'write', 'writelines'] >>> f.encoding # interpréteur Python lancé depuis une console Windows 'cp1252' Comme précisé plus haut, c'est cependant une bonne pratique de **préciser explicitement l'encodage du fichier** dans les paramètres de la fonction :func:`open`. C'est même indispensable lorsque le fichier comporte des **caractères accentués**. Le nom du fichier est accessible dans l'attribut :attr:`~io.FileIO.name`:: .. code-block:: python >>> f = open('lecid.txt', mode='r', encoding='utf8') >>> f.encoding 'utf8' >>> f.name 'lecid.txt' Fermeture du fichier -------------------- Fermer un fichier c'est rompre la connexion avec la ressource contenant les données, et donc libérer cette dernière pour le système d'exploitation. La fonction :meth:`~io.IOBase.close` permet de fermer le fichier une fois la lecture effectuée. .. code-block:: python >>> f.closed False >>> f.close() >>> f.closed True La bonne pratique de manipulation d'un fichier consiste donc en 3 étapes : #. ouvrir le fichier #. réaliser les opérations de lecture/écriture #. fermer le fichier La construction ``with`` ------------------------ L'action d'ouverture-fermeture d'un fichier est non seulement très courante mais la fermeture du fichier, après les opérations de lecture/écriture, nécessaire. C'est une façon concise, efficace et élégante de fermer le fichier après les opérations de lecture/écriture. .. code-block:: python >>> with open('lecid.txt', mode='r', encoding='utf8') as f: ... s = f.read() ... >>> f.closed True Cette méthode est sure car le fichier est automatiquement fermé après l'exécution des instructions du bloc ``with``. ``with`` est un *context manager*. Il permet de définir des opérations à exécuter : - avant les instructions du bloc ; - après les instructions du bloc. .. note:: ``with`` est utilisé ici avec un objet de type :class:`io.TextIOWrapper` mais peut être employé avec tout objet implémentant les méthodes : - :meth:`__enter__` ; - et :meth:`__exit__`. Lecture du contenu ------------------ Il existe plusieurs façon de lire le contenu d'un fichier: On peut récupérer l'intégralité du contenu dans une chaîne de caractères avec la méthode :meth:`~io.TextIOBase.read` comme dans l'exemple ci dessus. .. code-block:: python >>> s = f.read() >>> type(s) >>> len(s) # nombre de caractères 95302 >>> s[-46:] 'Laisse faire le temps, ta vaillance et ton roi' Un autre appel à :meth:`~io.TextIOBase.read` retourne la chaîne vide (car tout le fichier a été lu par l'instruction précédente). .. code-block:: python >>> s = f.read() >>> s '' On peut aussi récupérer l'intégralité du contenu dans une liste de chaînes de caractères avec :meth:`~io.IOBase.readlines`. .. code-block:: python >>> with open('lecid.txt', mode='r', encoding='utf8') as f: ... l = f.readlines() >>> type(l) >>> len(l) # nombre de lignes 3236 >>> l[-1] 'Laisse faire le temps, ta vaillance et ton roi' >>> for i in l[785:787]: ... print(i, end='') ... Je suis jeune, il est vrai ; mais aux âmes bien nées La valeur n'attend point le nombre des années. On peut également lire le fichier ligne par ligne avec :meth:`~io.IOBase.readline`. .. code-block:: python >>> f.readline() 'LE CID\n' >>> f.readline() '======\n' >>> f.readline() '\n' :meth:`~io.IOBase.readline` n'a d'intérêt qu'encapsulé dans une boucle ``for`` ou ``while`` simple et que si la taille du fichier est trop importante pour un chargement total en mémoire. Sinon :meth:`~io.IOBase.readlines` sera préférée. .. code-block:: python >>> with open('lecid.txt', mode='r', encoding='utf8') as f: ... for i in range(10): ... print(f.readline()) Ecriture dans un fichier ------------------------ On écrit dans un fichier avec la méthode :meth:`~io.TextIOBase.write`. Il faut auparavant ouvrir le fichier en écriture (``mode='w'``). .. code-block:: python >>> with open('file.txt', mode='w', encoding='utf8') as f: ... f.write("M. Churchill, si j'étais votre femme, je mettrais du poison dans votre café !\n") ... f.write("Lady Astor, si j'étais votre mari, je le boirais !") ... 78 50 La méthode :meth:`~io.TextIOBase.write` retourne le nombre de caractères écrits dans le fichier. On peut également utiliser la méthode :meth:`~io.IOBase.writelines` qui permet d'écrire dans un fichier un ensemble de chaines de caractères stockées dans une liste. Les fichiers csv ---------------- Le format ``csv`` `Comma Separated Value `_ est couramment utilisé pour échanger des données au format texte. Le module :mod:`csv` de Python dispose de méthodes spécifiques pour lire ce genre de format. On considère le fichier des observations météo de la station d'Edimbourg en Ecosse :download:`Edinburgh_2015_Oct.csv `. Ouvrir ce fichier avec un éditeur de texte pour en observer la structure. Pour en lire le contenu, Python utilise la fonction :func:`~csv.reader` qui retourne un itérable. .. code-block:: python >>> with open('Edinburgh_2015_Oct.csv', 'r') as f: ... r = csv.reader(f) ... l = list(r) # l'itérable est converti en liste ... Le format `Comma Separated Value `_ ne fixe pas le délimiteur utilisé pour séparer les données. Pour le format anglo-saxon, la virgule ``,`` est utilisée. La taille de la liste construite à partir de l'objet retourné par :func:`~csv.reader` est égale au nombre de lignes du fichier. La première ligne contient l'en tête. A partir de la seconde, on trouve les données. .. code-block:: python >>> r.dialect.delimiter ',' >>> len(l) 44703 >>> l[0] ['date-time', 'atmospheric pressure (mBar)', 'rainfall (mm)', 'wind speed (m/s)', 'wind direction (degrees)', 'surface temperature (C)', 'relative humidity (%)', 'solar flux (Kw/m2)', 'battery (V)'] >>> l[1] # la première ligne de données ['2015/10/01 00:01', '1035', '0', '0', '0', '9.28', '93.9', '0', '13.77'] .. admonition:: A expérimenter... On considère le fichier :download:`synop.2015110112.csv ` qui contient des données produites par `Météo France `_ au format ``csv``. Ouvrir ce fichier avec un éditeur de texte pour en observer la structure. Le séparateur décimal français étant la virgule ``,``, cette dernière ne peut pas être utilisée comme délimiteur. La convention française est d'utiliser le point virgule ``;``. Il faut pour cela utiliser le paramètre :attr:`delimiter` de la fonction :func:`~csv.reader`. .. code-block:: python :emphasize-lines: 2 >>> with open('synop.2015110112.csv', 'r') as f: ... r = csv.reader(f, delimiter=';') ... l = list(r) Combien de lignes comporte le fichier ? Combien y a t-il de données par ligne ? .. note:: Le travail avec les fichiers ``csv`` est facilité dans Visual Studio Code par `l'extension Rainbow CSV `_. Elle s'installe à partir du menu :command:`Affichage > Extensions`. Ce qu'il faut retenir --------------------- .. quiz:: quizz-08 :title: Les fichiers - :quiz:`{"type":"FB","answer":"open", "size":5}` est la fonction qui permet d'ouvrir un fichier - :quiz:`{"type":"FB","answer":"close", "size":5}` est la fonction qui permet de fermer un fichier - La fermeture de fichier est automatique si on utilise la construction :quiz:`{"type":"FB","answer":"with", "size":5}` - :quiz:`{"type":"TF","answer":"T"}` On peut ouvrir un fichier en ne précisant que son nom - :quiz:`{"type":"TF","answer":"F"}` Si on ne le précise pas le mode d'ouverture par défaut d'un fichier est en écriture - :quiz:`{"type":"TF","answer":"T"}` Lorsqu'on ouvre un fichier, c'est une bonne pratique de préciser son mode d'ouverture, lecture ou écriture - :quiz:`{"type":"TF","answer":"T"}` Si on ne le précise pas l'encodage par défaut lors de l'ouverture d'un fichier est celui du système d'exploitation - :quiz:`{"type":"TF","answer":"T"}` Lorsqu'on ouvre un fichier, c'est une bonne pratique de préciser son encodage - :quiz:`{"type":"FB","answer":"read readline readlines","flags":"sequence"}` sont les 3 méthodes de lecture d'un fichier texte - :quiz:`{"type":"TF","answer":"T"}` La méthode :meth:`~io.IOBase.read` retourne une chaine de caractères - :quiz:`{"type":"TF","answer":"T"}` La méthode :meth:`~io.IOBase.readlines` retourne une liste de chaine de caractères - :quiz:`{"type":"FB","answer":"write writelines","flags":"sequence"}` sont les 2 méthodes d'écriture dans un fichier texte - :quiz:`{"type":"TF","answer":"T"}` On peut itérer sur l'objet retourné par la fonction :func:`open` - :quiz:`{"type":"TF","answer":"T"}` Un fichier ``csv`` est un fichier texte - :quiz:`{"type":"TF","answer":"F"}` Un fichier ``csv`` ne peut être lu qu'avec le module :mod:`csv` - :quiz:`{"type":"TF","answer":"T"}` Le module :mod:`csv` facilite la lecture d'un fichier ``csv`` - :quiz:`{"type":"TF","answer":"F"}` La première ligne d'un fichier ``csv`` comporte des données - :quiz:`{"type":"TF","answer":"F"}` Le délimiteur d'un fichier ``csv`` est toujours le caractère ``,`` - :quiz:`{"type":"TF","answer":"F"}` Le délimiteur d'un fichier ``csv`` est toujours le caractère ``;`` - :quiz:`{"type":"TF","answer":"T"}` On peut préciser le délimiteur d'un fichier ``csv`` à la lecture .. _exercice-fichiers: .. exercice:: Lecture de données Il est nécessaire que VS Code soit démarré sur la machine hôte, pointe vers le répertoire ``e3-programmation-labs-student`` et que le container Docker soit lancé. Le répertoire concerné est ``lab_python_lecture_donnees``. Lisez attentivement le fichier ``README.md`` pour comprendre la consigne. Il est efficace d'y accéder depuis le dépôt distant pour profiter du formatage. Une fois l'exercice terminé, effectuer une :ref:`revue de code `, ajouter la :ref:`documentation `, et s'assurer que les repos local et distant soient correctement :ref:`synchronisés `.