Summary

Ce document fournit un aperçu des fichiers Python. Il explique comment les fichiers sont gérés dans le contexte du développement logiciel Python, en discutant de la manipulation, du stockage et de la gestion des données stockées dans les fichiers. Le document traite également des différences entre les conventions de nommage des fichiers sur différents systèmes d'exploitation, comme Windows et Unix/Linux.

Full Transcript

Année académique 2024-2025 16 Les fichiers 16.1 Accès aux fichiers à partir du code Python L’un des problèmes les plus courants dans le travail du développeur est de traiter les données stockées dans des fichiers alors que les fichiers sont généralement stockés physiquement à l’aide de périphériqu...

Année académique 2024-2025 16 Les fichiers 16.1 Accès aux fichiers à partir du code Python L’un des problèmes les plus courants dans le travail du développeur est de traiter les données stockées dans des fichiers alors que les fichiers sont généralement stockés physiquement à l’aide de périphériques de stockage : disques durs, optiques, réseau ou SSD. Il est facile d’imaginer un programme qui trie 20 numéros, et il est tout aussi facile d’imaginer l’utilisateur de ce programme entrant ces vingt numéros directement à partir du clavier. Il est beaucoup plus difficile d’imaginer la même tâche lorsqu’il y a 20 000 numéros à trier et qu’il n’y a pas un seul utilisateur capable d’entrer ces numéros sans faire d’erreur. Il est beaucoup plus facile d’imaginer que ces nombres sont stockés dans un fichier qui est lu par le programme. Le programme trie les nombres et ne les envoie pas à l’écran, mais crée un nouveau fichier et y enregistre la séquence de nombres triée. Si nous voulons implémenter une base de données simple, la seule façon de stocker les informations entre les exécutions du programme est de les enregistrer dans un fichier (ou des fichiers si votre base de données est plus complexe). En principe, tout problème de programmation non simple repose sur l’utilisation de fichiers, qu’il s’agisse de traiter des images (stockées dans des fichiers), de multiplier les matrices (stockées dans des fichiers) ou de calculer les salaires et les impôts (lecture de données stockées dans des fichiers). Vous vous demandez peut-être pourquoi nous avons attendu jusqu’à maintenant pour vous montrer ce point. La réponse est très simple: la façon dont Python accède et traite les fichiers est implémentée à l’aide d’un ensemble cohérent d’objets. Et comme nous n’avons pas encore vu ce qu’est un objet, c’est un petit peu complexe de l’aborder facilement. 16.2 Noms de fichiers Différents systèmes d’exploitation peuvent traiter les fichiers de différentes manières. Par exemple, Windows utilise une convention de nommage différente de celle adoptée dans les systèmes Unix/Linux. 305 Année académique 2024-2025 Si nous utilisons la notion de nom de fichier canonique (un nom qui définit de manière unique l’emplacement du fichier quel que soit son niveau dans l’arborescence des répertoires), nous pouvons nous rendre compte que ces noms sont différents sous Windows et sous Unix/Linux : Comme vous pouvez le voir, les systèmes dérivés d’Unix/Linux n’utilisent pas la lettre de lecteur de disque (par exemple, C:) et tous les répertoires se développent à partir d’un répertoire racine appelé /, tandis que les systèmes Windows reconnaissent le répertoire racine comme \. En outre, les noms de fichiers système Unix/Linux sont sensibles à la casse. Les systèmes Windows stockent la casse des lettres utilisées dans le nom de fichier, mais ne font aucune distinction entre leurs casses. Cela signifie que ces deux chaînes: ThisIsTheNameOfTheFile et thisisthenameofthefile décrivent deux fichiers différents dans les systèmes Unix/Linux, mais portent le même nom pour un seul fichier dans les systèmes Windows. La différence principale et la plus frappante est que vous devez utiliser deux séparateurs différents pour les noms de répertoire: \ sous Windows et / sous Unix/Linux. Cette différence n’est pas très importante pour l’utilisateur normal, mais est très importante lors de l’écriture de programmes en Python. Pour comprendre pourquoi, essayez de rappeler le rôle très spécifique joué par le \ à l’intérieur des chaînes Python. Supposons que vous soyez intéressé par un fichier particulier situé dans le répertoire dir et nommé « fichier ». Supposons également que vous souhaitiez affecter à une chaîne le nom du fichier. Dans les systèmes Unix/Linux, elle peut se présenter comme suit : name = « /dir/fichier » Mais si vous essayez de coder pour le système Windows: name = « \dir\fichier » Vous aurez une mauvaise surprise: soit Python générera une erreur, soit l’exécution du programme se comportera étrangement, comme si le nom du fichier avait été déformé d’une manière ou d’une autre. En fait, ce n’est pas étrange du tout, mais tout à fait évident et naturel. Python utilise le \ comme caractère d’échappement (comme \n). Cela signifie que les noms de fichiers Windows doivent être écrits comme suit : name = « \\dir\\fichier » 306 Année académique 2024-2025 Heureusement, il existe également une autre solution. Python est assez intelligent pour pouvoir convertir les barres obliques en barres obliques inverses chaque fois qu’il découvre que cela est requis par le système d’exploitation. Cela signifie que l’une des affectations suivantes : name = « /dir/fichier » name = « c:/dir/fichier » fonctionnera également avec Windows. Tout programme écrit en Python (et pas seulement en Python, car cette convention s’applique à pratiquement tous les langages de programmation) ne communique pas directement avec les fichiers, mais à travers certaines entités abstraites nommées différemment dans différents langages ou environnements, les termes les plus utilisés sont les descripteurs ou les flux (nous les utiliserons comme synonymes ici). Le programmeur, ayant un ensemble plus ou moins riche de fonctions/méthodes, est capable d’effectuer certaines opérations sur le flux, qui affectent les fichiers réels en utilisant les mécanismes contenus dans le noyau du système d’exploitation. De cette façon, vous pouvez implémenter le processus d’accès à n’importe quel fichier, même lorsque le nom du fichier est inconnu au moment de l’écriture du programme. Les opérations effectuées avec le flux abstrait reflètent les activités liées au fichier physique. Pour connecter (lier) le flux au fichier, il est nécessaire d’effectuer une opération explicite. L’opération de connexion du flux à un fichier est appelée ouverture du fichier, tandis que la déconnexion de ce lien est appelée fermeture du fichier. Par conséquent, la conclusion est que la toute première opération effectuée sur le flux est toujours open et la dernière est close. Le programme, en effet, est libre de manipuler le flux entre ces deux événements et de manipuler le fichier associé. Cette liberté est limitée, bien sûr, par les caractéristiques physiques du fichier et la manière dont le fichier a été ouvert. Disons à nouveau que l’ouverture du flux peut échouer, et cela peut arriver pour plusieurs raisons: la plus courante est l’absence d’un fichier avec le nom spécifié. Il peut également arriver que le fichier physique existe, mais que le programme ne soit pas autorisé à l’ouvrir. Il y a aussi le risque que le programme ait ouvert trop de flux, et le système d’exploitation spécifique peut ne pas permettre l’ouverture simultanée de plus de n fichiers (par exemple, 200). Un programme bien écrit doit détecter ces ouvertures défectueuses et réagir en conséquence. 307 Année académique 2024-2025 16.3 Flux de fichiers L’ouverture du flux n’est pas seulement associée au fichier, mais doit également déclarer la manière dont le flux sera traité. Cette déclaration est appelée « open mode ». Si l’ouverture réussit, le programme ne sera autorisé à effectuer que les opérations compatibles avec le mode ouvert déclaré. Deux opérations de base sont effectuées sur le flux : Lire à partir du flux : les parties des données sont extraites du fichier et placées dans une zone mémoire gérée par le programme (par exemple, une variable); Écrire dans le flux : les parties des données de la mémoire (par exemple, une variable) sont transférées dans le fichier. Trois modes de base sont utilisés pour ouvrir le flux : Mode lecture : un flux ouvert dans ce mode autorise uniquement les opérations de lecture ; toute tentative d’écriture dans le flux entraînera une exception (l’exception est nommée UnsupportedOperation, qui hérite d’OSError et de ValueError et provient du module io) ; Mode d’écriture : un flux ouvert dans ce mode n’autorise que les opérations d’écriture ; toute tentative de lecture du flux entraînera l’exception mentionnée ci-dessus ; Mode de mise à jour : un flux ouvert dans ce mode permet à la fois les écritures et les lectures. Avant de discuter de la façon de manipuler les flux, nous vous devons quelques explications. Le flux se comporte presque comme un magnétophone. Lorsque vous lisez quelque chose à partir d’un flux, une tête virtuelle se déplace sur le flux en fonction du nombre d’octets transférés à partir du flux. Lorsque vous écrivez quelque chose dans le flux, la même tête se déplace le long du flux en enregistrant les données de la mémoire. Chaque fois que nous parlons de lire et d’écrire dans le flux, essayez d’imaginer cette analogie. Les livres de programmation se réfèrent à ce mécanisme comme la position actuelle du fichier, et nous utiliserons également ce terme. Il est maintenant nécessaire de vous montrer l’objet responsable de la représentation des flux dans les programmes. 308 Année académique 2024-2025 16.4 Gestion des fichiers Python suppose que chaque fichier est caché derrière un objet d’une classe adéquate. Bien sûr, il est difficile de ne pas se demander comment interpréter le mot adéquat. Et encore plus de parler d’objet et de classe mais ces deux mots on y reviendra. Les fichiers peuvent être traités de différentes manières, certaines d’entre elles dépendent du contenu du fichier, d’autres des intentions du programmeur. Dans tous les cas, différents fichiers peuvent nécessiter différents ensembles d’opérations et se comporter de différentes manières. Un objet d’une classe adéquate est créé lorsque vous ouvrez le fichier et l’annihilez au moment de la fermeture. Entre ces deux événements, vous pouvez utiliser l’objet pour spécifier les opérations à effectuer sur un flux particulier. Les opérations que vous êtes autorisé à utiliser sont imposées par la façon dont vous avez ouvert le fichier. Ok, ce n’est pas encore très clair, mais retenez qu’un objet c’est un peu comme une variable avec des fonctions qui lui sont propres (ou une liste). En général, l’objet provient de l’une des classes présentées ici : Remarque : vous n’utilisez jamais de constructeurs(voir Q2) pour donner vie à ces objets. La seule façon de les obtenir est d’invoquer la fonction nommée open(). La fonction analyse les arguments que vous avez fournis et crée automatiquement l’objet requis. Si vous souhaitez vous débarrasser du fichier, vous appelez la méthode nommée close(). L’appel rompra la connexion à l’objet et au fichier et supprimera l’objet. Pour nos besoins, nous nous intéresserons uniquement aux flux représentés par les objets BufferIOBase et TextIOBase. Vous comprendrez pourquoi bientôt. En raison du type de contenu du flux, tous les flux sont divisés en flux texte et binaires. Les flux de texte sont structurés en lignes; c’est-à-dire qu’ils contiennent des caractères typographiques (lettres, chiffres, ponctuation, etc.) disposés en colonnes (lignes), comme on le voit à l’œil nu lorsque vous regardez le contenu du fichier dans l’éditeur. Ce fichier est écrit (ou lu) principalement caractère par caractère, ou ligne par ligne. 309 Année académique 2024-2025 Les flux binaires ne contiennent pas de texte mais une séquence d’octets de n’importe quelle valeur. Cette séquence peut être, par exemple, un programme exécutable, une image, un clip audio ou vidéo, un fichier de base de données, etc. Étant donné que ces fichiers ne contiennent pas de lignes, les lectures et écritures concernent des parties de données de toute taille. Par conséquent, les données sont lues / écrites octet par octet, ou bloc par bloc, où la taille du bloc varie généralement d’une à une valeur choisie arbitrairement. Vient ensuite un problème subtil. Dans les systèmes Unix/Linux, les extrémités de ligne sont marquées par un seul caractère nommé LF (code ASCII 10) désigné dans les programmes Python comme \n. D’autres systèmes d’exploitation, en particulier ceux dérivés du système préhistorique CP/M (qui s’applique également aux systèmes de la famille Windows) utilisent une convention différente: la fin de ligne est marquée par une paire de caractères, CR et LF (codes ASCII 13 et 10) qui peuvent être codés comme \r\n. Cette ambiguïté peut avoir diverses conséquences désagréables. Si vous créez un programme responsable du traitement d’un fichier texte, et qu’il est écrit pour Windows, vous pouvez reconnaître les extrémités des lignes en trouvant les caractères \r\n, mais le même programme exécuté dans un environnement Unix/Linux sera complètement inutile, et vice versa. De telles fonctionnalités indésirables du programme, qui empêchent ou entravent l’utilisation du programme dans différents environnements, sont appelées non- portable. De même, la caractéristique du programme permettant l’exécution dans différents environnements est appelée portabilité. Un programme doté d’un tel trait est appelé un programme portable. Étant donné que les problèmes de portabilité étaient (et sont toujours) très graves, une décision a été prise pour résoudre définitivement le problème d’une manière qui n’engage pas l’attention du développeur. Cela a été fait au niveau des classes, qui sont responsables de la lecture et de l’écriture des caractères vers et depuis le flux. Il fonctionne de la manière suivante: Lorsque le flux est ouvert et que les données du fichier associé sont traitées sous forme de texte, il passe en mode texte ; Lors de la lecture/écriture de lignes de/vers le fichier associé, rien de spécial ne se produit dans l’environnement Unix, mais lorsque les mêmes opérations sont effectuées dans l’environnement Windows, un processus appelé traduction des caractères de nouvelle ligne se produit : lorsque vous lisez une ligne du fichier, chaque paire de caractères \r\n est remplacée par un seul caractère \n, et vice versa; 310 Année académique 2024-2025 Le mécanisme est complètement transparent pour le programme, qui peut être écrit comme s’il était destiné au traitement de fichiers texte Unix/Linux uniquement; le code source exécuté dans un environnement Windows fonctionnera également correctement; Lorsque le flux est ouvert, son contenu est pris tel quel, sans aucune conversion, aucun octet n’est ajouté ou omis. 16.5 Ouverture des flux L’ouverture du flux est effectuée par une fonction qui peut être invoquée de la manière suivante : stream = open(file, mode = 'r', encoding = None) Analysons-la ligne : le nom de la fonction (open) parle de lui-même ; si l’ouverture réussit, la fonction renvoie un objet stream ; sinon, une exception est déclenchée (par exemple, FileNotFoundError si le fichier que vous allez lire n’existe pas) ; le premier paramètre de la fonction (file) spécifie le nom du fichier à associer au flux ; le deuxième paramètre (mode) spécifie le mode ouvert utilisé pour le flux; c’est une chaîne remplie d’une séquence de caractères, et chacun d’eux a sa propre signification particulière (plus de détails bientôt); le troisième paramètre (encoding) spécifie le type de codage (par exemple, UTF-8 lorsque vous travaillez avec des fichiers texte) L’ouverture doit être la toute première opération effectuée sur le flux. Remarque: les arguments de mode et d’encodage peuvent être omis, leurs valeurs par défaut sont alors supposées. Le mode d’ouverture par défaut est la lecture en mode texte, tandis que l’encodage par défaut dépend de la plate- forme utilisée. 311 Année académique 2024-2025 16.5.1 Ouverture des flux : modes r : lecture Le flux sera ouvert en mode lecture ; Le fichier associé au flux doit exister et doit être lisible, sinon la fonction open() déclenche une exception. w : écriture Le flux sera ouvert en mode écriture ; Le fichier associé au flux n’a pas besoin d’exister ; s’il n’existe pas, il sera créé ; s’il existe, il sera tronqué à la longueur zéro (effacé) ; si la création n’est pas possible (par exemple, en raison d’autorisations système), la fonction open() déclenche une exception. a : Ajouter Le flux sera ouvert en mode append ; Le fichier associé au flux n’a pas besoin d’exister ; s’il n’existe pas, il sera créé ; s’il existe, la tête d’enregistrement virtuelle sera définie à la fin du fichier (le contenu précédent du fichier reste intact). r+ : lecture et mise à jour Le flux sera ouvert en mode lecture et mise à jour; Le fichier associé au flux doit exister et doit être accessible en écriture, sinon la fonction open() déclenche une exception ; Les opérations de lecture et d’écriture sont autorisées pour le flux. w+ : écriture et mise à jour Le flux sera ouvert en mode écriture et mise à jour ; le fichier associé au flux n’a pas besoin d’exister ; s’il n’existe pas, il sera créé ; le contenu précédent du fichier reste intact ; Les opérations de lecture et d’écriture sont autorisées pour le flux. 16.5.2 Sélection des modes texte et binaire S’il y a une lettre b à la fin de la chaîne de mode, cela signifie que le flux doit être ouvert en mode binaire. Si la chaîne de mode se termine par une lettre t, le flux est ouvert en mode texte. Le mode texte est le comportement par défaut supposé lorsqu’aucun spécificateur de mode binaire/texte n’est utilisé. Enfin, l’ouverture réussie du fichier définira la position actuelle du fichier (la tête de lecture/écriture virtuelle) avant le premier octet du fichier si le mode n’est pas a et après le dernier octet du fichier si le mode est défini sur a. 312 Année académique 2024-2025 Mode texte Mode binaire Description Rt Rb lire Wt Wb écrire à De ajouter R+T R+B Lire et mettre à jour W+T W+B Écrire et mettre à jour 16.5.3 Ouverture du flux pour la première fois Imaginez que nous voulions développer un programme qui lit le contenu du fichier texte nommé : C:\Users\User\Desktop\file.txt. Comment ouvrir ce fichier pour la lecture? Voici l’extrait de code pertinent : try: stream = open("C:\Users\User\Desktop\file.txt", "rt") # Le traitement à lieu ici. stream.close() except Exception as exc: print("Cannot open the file:", exc) Qu’est-ce qui se passe? Nous utilisons le bloc try-except car nous voulons gérer les erreurs d’exécution en douceur; Nous utilisons la fonction open() pour essayer d’ouvrir le fichier spécifié (notez la façon dont nous avons spécifié le nom du fichier) Le mode ouvert est défini comme du texte à lire (comme le texte est le paramètre par défaut, nous pouvons ignorer la chaîne t en mode) En cas de succès, nous obtenons un objet de la fonction open() et nous l’affectons à la variable stream; Si open() échoue, nous gérons l’exception en imprimant les informations d’erreur complètes (il est utile de savoir ce qu’il s’est passé exactement) 313 Année académique 2024-2025 16.5.4 Flux pré-ouverts Nous avons dit précédemment que toute opération de flux doit être précédée de l’appel de la fonction open(). Il y a trois exceptions bien définies à la règle. Lorsque notre programme commence, les trois flux sont déjà ouverts et ne nécessitent aucune préparation supplémentaire. De plus, votre programme peut utiliser ces flux explicitement si vous prenez soin d’importer le module sys : import sys Parce que c’est dans ce module que la déclaration des trois flux est placée. Les noms de ces flux sont : sys.stdin, sys.stdout et sys.stderr. Analysons-les : sys.stdin o stdin (en tant qu’entrée standard) o Le flux stdin est normalement associé au clavier, pré-ouvert pour la lecture et considéré comme la source de données principale pour les programmes en cours d’exécution; o La fonction input() lit les données de Stdin par défaut. sys.stdout o stdout (en sortie standard) o Le flux stdout est normalement associé à l’écran, pré-ouvert pour l’écriture, considéré comme la cible principale pour la sortie de données par le programme en cours d’exécution; o La fonction print() envoie les données au flux stdout. sys.stderr o stderr (en tant que sortie d’erreur standard) o Le flux stderr est normalement associé à l’écran, pré-ouvert à l’écriture, considéré comme le lieu principal où le programme en cours d’exécution doit envoyer des informations sur les erreurs rencontrées au cours de son travail; o Nous n’avons présenté aucune méthode pour envoyer les données à ce flux (nous le ferons bientôt, promis) o La séparation des stdout (résultats utiles produits par le programme) des stderr (messages d’erreur, indéniablement utiles mais ne donnant pas de résultats) donne la possibilité de rediriger ces deux types d’informations vers les différentes cibles. Une discussion plus approfondie de cette question dépasse la portée de notre cours. Le manuel du système d’exploitation fournira plus d’informations sur ces questions. 314 Année académique 2024-2025 16.5.5 Fermeture des flux La dernière opération effectuée sur un flux (cela n’inclut pas les flux stdin , stdout et stderr qui n’en ont pas besoin) est de les fermer. Cette action est effectuée par une méthode appelée à partir de l’objet ouvert dans le flux : stream.close(). Le nom de la fonction est auto-commenté: close() La fonction attend exactement zéro argument ; Le flux n’a pas besoin d’être ouvert La fonction ne renvoie rien mais déclenche l’exception IOError en cas d’erreur ; La plupart des développeurs pensent que la fonction close() réussit toujours et qu’il n’est donc pas nécessaire de vérifier si elle a fait sa tâche correctement. Cette croyance n’est que partiellement justifiée. Si le flux a été ouvert pour l’écriture et qu’une série d’opérations d’écriture ont été effectuées, il peut arriver que les données envoyées au flux n’aient pas encore été transférées vers le périphérique physique (en raison d’un mécanisme appelé mise en cache ou mise en mémoire tampon). Étant donné que la fermeture du flux force les tampons à se vider, il se peut que cela échoue et donc que le close() échoue aussi. Nous avons déjà mentionné les défaillances causées par des fonctions fonctionnant avec des flux, mais nous n’avons pas parlé de la façon dont nous pouvons identifier exactement la cause de cette défaillance. La possibilité de faire un diagnostic existe et est fournie par l’une des composantes d’exception des flux. Wait and see, cela arrive. 16.5.6 Diagnostic des problèmes de flux L’objet IOError est équipé d’une propriété nommée errno (le nom vient de l’expression numéro d’erreur) et vous pouvez y accéder comme suit : try: # Some stream operations. except IOError as exc: print(exc.errno) Jetons un coup d’œil à quelques constantes sélectionnées utiles pour détecter les erreurs de flux: errno.EACCES Autorisation refusée L’erreur se produit lorsque vous essayez, par exemple, d’ouvrir un fichier avec l’attribut lecture seule pour l’écriture. errno.EBADF Numéro de fichier incorrect L’erreur se produit lorsque vous essayez, par exemple, d’utiliser un flux non ouvert. 315 Année académique 2024-2025 errno.EEXIST Fichier existe L’erreur se produit lorsque vous essayez, par exemple, de renommer un fichier avec son nom précédent. errno.EFBIG Fichier trop volumineux L’erreur se produit lorsque vous essayez de créer un fichier qui est plus grand que le maximum autorisé par le système d’exploitation. errno.EISDIR est un répertoire L’erreur se produit lorsque vous essayez de traiter un nom de répertoire comme le nom d’un fichier ordinaire. errno.EMFILE Trop de fichiers ouverts L’erreur se produit lorsque vous essayez d’ouvrir simultanément plus de flux que ce qui est acceptable pour votre système d’exploitation. errno.ENOENT Aucun fichier ou répertoire de ce type L’erreur se produit lorsque vous essayez d’accéder à un fichier/répertoire inexistant. errno.ENOSPC Pas d’espace sur l’appareil L’erreur se produit lorsqu’il n’y a pas d’espace libre sur le média. La liste complète est beaucoup plus longue (elle inclut également des codes d’erreur non liés au traitement du flux). Mais nous pensons que cela est déjà bien pour un cours d’introduction. 16.5.7 Diagnostic des problèmes de flux, la suite Si vous êtes un programmeur très prudent, vous pouvez ressentir le besoin d’utiliser la séquence d’instructions similaire à celles présentées ci-dessous. import errno try: s = open("c:/users/user/Desktop/file.txt", "rt") # Proccessus ici. s.close() except Exception as exc: if exc.errno == errno.ENOENT: print("The file doesn't exist.") elif exc.errno == errno.EMFILE: print("You've opened too many files.") else: print("The error number is:", exc.errno) 316 Année académique 2024-2025 Heureusement, il existe une fonction qui peut simplifier considérablement le code de gestion des erreurs que vous pouvez voir. Son nom est strerror(), et elle vient du module os et n’attend qu’un seul argument : un numéro d’erreur. Son rôle est simple : vous donnez un numéro d’erreur et obtenir une chaîne décrivant la signification de l’erreur. Remarque: si vous passez un code d’erreur inexistant (un nombre qui n’est lié à aucune erreur réelle), la fonction déclenchera l’exception ValueError. Nous pouvons donc simplifier notre code de la manière suivante: from os import strerror try: s = open("c:/users/user/Desktop/file.txt", "rt") # Actual processing goes here. s.close() except Exception as exc: print("The file could not be opened:", strerror(exc.errno)) 16.5.8 Principaux points à retenir 1. Un fichier doit être ouvert avant de pouvoir être traité par un programme, et il doit être fermé lorsque le traitement est terminé. L’ouverture du fichier l’associe au flux, et est une représentation abstraite des données physiques stockées sur le support. La façon dont le flux est traité est appelée open mode. Trois modes ouverts existent : Mode de lecture : seules les opérations de lecture sont autorisées; Mode d’écriture : seules les opérations d’écriture sont autorisées; Mode de mise à jour : les écritures et les lectures sont autorisées. 2. Selon le contenu du fichier physique, différentes classes Python peuvent être utilisées pour traiter les fichiers. En général, BufferedIOBase est capable de traiter n’importe quel fichier, tandis que TextIOBase est une classe spécialisée dédiée au traitement des fichiers texte (c’est-à-dire des fichiers contenant des textes visibles par l’homme divisés en lignes à l’aide de marqueurs de nouvelle ligne). Ainsi, les flux peuvent être divisés en binaires et textes. 3. La syntaxe suivante de la fonction open() est utilisée pour ouvrir un fichier : open(file_name, mode=open_mode, encoding=text_encoding) L’appel crée un objet stream et l’associe au fichier nommé file_name, en utilisant l’ open_mode spécifié et en définissant le text_encoding spécifié, où il déclenche une exception en cas d’erreur. 317 Année académique 2024-2025 4. Trois volets prédéfinis sont déjà ouverts au démarrage du programme : sys.stdin : entrée standard; sys.stdout : sortie standard; sys.stderr : sortie d’erreur standard. 4. L’objet d’exception IOError, créé lorsque des opérations de fichier échouent (y compris les opérations ouvertes), contient une propriété nommée errno, qui contient le code d’achèvement de l’action ayant échoué. Utilisez cette valeur pour diagnostiquer le problème. Exercice 1 Comment encoder l’argument mode d’une fonction open() si vous allez créer un nouveau fichier texte que vous voulez remplir uniquement avec un texte ? Exercice 2 Quelle est la signification de la valeur représentée par errno.EACCES ? Exercice 3 Quelle est la sortie attendue du code suivant, en supposant que le fichier nommé file n’existe pas ? import errno try: stream = open("file", "rb") print("exists") stream.close() except IOError as error: if error.errno == errno.ENOENT: print("absent") else: print("unknown") 318 Année académique 2024-2025 16.6 Le travail sur les fichiers texte 16.6.1 Traitement des fichiers texte Dans ce chapitre, nous allons préparer un fichier texte simple avec un contenu court et simple. Nous allons vous montrer quelques techniques de base que vous pouvez utiliser pour lire le contenu du fichier afin de les traiter. Le traitement sera très simple, vous allez copier le contenu du fichier sur la console et compter tous les caractères que le programme a lus. Mais rappelez-vous que notre compréhension d’un fichier texte est très stricte. Dans notre vision des choses, il s’agit d’un fichier texte brut, il ne peut contenir que du texte, sans aucune décoration supplémentaire (formatage, polices différentes, etc.). C’est pourquoi vous devriez éviter de créer le fichier à l’aide d’un traitement de texte avancé comme MS Word, LibreOffice Writer, ou un logiciel du genre. Utilisez ceux de bases de votre système d’exploitation: bloc-notes, vim, gedit, etc. Si vos fichiers texte contiennent des caractères nationaux non couverts par le jeu de caractères ASCII standard, vous aurez peut-être besoin d’une étape supplémentaire. Votre invocation de fonction open() peut nécessiter un argument désignant un codage de texte spécifique. Par exemple, si vous utilisez un système d’exploitation Unix/Linux configuré pour utiliser UTF-8 comme paramètre à l’échelle du système, la fonction open() peut se présenter comme suit : stream = open('file.txt', 'rt', encoding='utf-8') où l’argument d’encodage doit être défini sur une valeur qui est une chaîne représentant le codage de texte approprié (UTF-8, dans notre cas). Exemple : # Opening tzop.txt in read mode, returning it as a file object: stream = open("tzop.txt", "rt", encoding = "utf-8") print(stream.read()) # printing the content of the file La lecture du contenu d’un fichier texte peut être effectuée à l’aide de plusieurs méthodes différentes, aucune d’entre elles n’est meilleure ou pire qu’une autre. C’est à vous de décider laquelle vous préférez. Certains d’entre elles seront parfois plus pratiques, et parfois plus gênantes. Faites preuve de souplesse. N’ayez pas peur de changer vos préférences. La plus basique de ces méthodes est celle offerte par la fonction read(), que vous avez pu voir en action précédmment. 319 Année académique 2024-2025 Si elle est appliquée à un fichier texte, la fonction est capable de : lire le nombre souhaité de caractères (dont un seul) dans le fichier et les renvoyer sous forme de chaîne ; lire tout le contenu du fichier et le renvoyer sous forme de chaîne ; S’il n’y a plus rien à lire (la tête de lecture virtuelle atteint la fin du fichier), la fonction renvoie une chaîne vide. Nous allons commencer par la variante la plus simple et utiliser un fichier nommé text.txt. Le fichier a le contenu suivant : Beau vaut mieux que laid. Explicite vaut mieux qu’implicite. Simple vaut mieux que complexe. Complexe vaut mieux que compliqué Depreter, Desmet, Scopel que choisir? Ça vous rappel quelque chose ?? Maintenant, regardez le code, et analysons-le. from os import strerror try: cnt = 0 s = open('text.txt', "rt") ch = s.read(1) while ch != '': print(ch, end='') cnt += 1 ch = s.read(1) s.close() print("\n\nCharacters in file:", cnt) except IOError as e: print("I/O error occurred: ", strerror(e.errno)) Le code est plutôt simple à reproduire : Utilisez le mécanisme try-except et ouvrez le fichier du nom prédéterminé (texte.txt dans notre cas) Essayez de lire le tout premier caractère du fichier (ch = s.read(1)) Si vous réussissez (ceci est prouvé par un résultat positif de la vérification de l’état while), sortez le caractère (notez l’argument end=, c’est important! Vous ne voulez pas passer à une nouvelle ligne après chaque caractère!); mettre à jour le compteur (cnt); Essayez de lire le caractère suivant et le processus se répète. 320 Année académique 2024-2025 Allons un peu plus loin Si vous êtes absolument sûr que la longueur du fichier est bonne et que vous pouvez lire le fichier entier dans la mémoire, vous pouvez le faire, la fonction read(), invoquée sans aucun argument ou avec un argument qui donne la valeur None, fera le travail pour vous. N’oubliez pas que la lecture d’un fichier de téraoctets à l’aide de cette méthode peut corrompre votre système d’exploitation. Ne vous attendez pas à des miracles, la mémoire de l’ordinateur n’est pas extensible. Regardez le code ci-desous. Qu’en pensez-vous ? from os import strerror try: cnt = 0 s = open('text.txt', "rt") content = s.read() for ch in content: print(ch, end='') cnt += 1 s.close() print("\n\nCharacters in file:", cnt) except IOError as e: print("I/O error occurred: ", strerror(e.errno)) Analysons-le : ouvrez le fichier comme précédemment; lire son contenu par un appel de fonction read(); Ensuite, traitez le texte, en l’itérant avec une boucle for et en mettant à jour la valeur du compteur à chaque tour de la boucle; Le résultat sera exactement le même que précédemment. 321 Année académique 2024-2025 16.6.2 Traitement des fichiers texte : readline() Si vous souhaitez traiter le contenu du fichier comme un ensemble de lignes, et non comme un tas de caractères, la méthode readline() vous y aidera. La méthode tente de lire une ligne complète de texte à partir du fichier et la renvoie sous forme de chaîne en cas de réussite. Sinon, il retourne une chaîne vide. Cela ouvre de nouvelles opportunités à vos talents, maintenant vous pouvez également compter facilement les lignes, pas seulement les caractères. Profitons-en. Regardez le code ci-dessous. from os import strerror try: ccnt = lcnt = 0 s = open('text.txt', 'rt') line = s.readline() while line != '': lcnt += 1 for ch in line: print(ch, end='') ccnt += 1 line = s.readline() s.close() print("\n\nCharacters in file:", ccnt) print("Lines in file: ", lcnt) except IOError as e: print("I/O error occurred:", strerror(e.errno)) Comme vous pouvez le voir, l’idée générale est exactement la même que dans les deux exemples précédents. 16.6.3 Traitement des fichiers texte : readlines() Une autre méthode, qui traite le fichier texte comme un ensemble de lignes, et non comme une suite de caractères, est readlines(). La méthode readlines(), lorsqu’elle est appelée sans arguments, tente de lire tout le contenu du fichier et renvoie une liste de chaînes, un élément par ligne de fichier. Si vous n’êtes pas sûr que la taille du fichier soit suffisamment petite et que vous ne voulez pas tester le système d’exploitation, vous pouvez convaincre la méthode readlines() de ne pas lire plus d’un nombre spécifié d’octets à la fois (la valeur renvoyée reste la même, c’est une liste de chaînes). N’hésitez pas à expérimenter avec l’exemple de code suivant pour comprendre le fonctionnement de la méthode readlines() : s = open("text.txt") print(s.readlines(20)) print(s.readlines(20)) print(s.readlines(20)) print(s.readlines(20)) s.close() 322 Année académique 2024-2025 La taille maximale de tampon d’entrée acceptée est transmise à la méthode en tant qu’argument. Vous pouvez vous attendre à ce que readlines() puisse traiter le contenu d’un fichier plus efficacement que readline(), car il peut être nécessaire de l’appeler moins de fois. Remarque : lorsqu’il n’y a rien à lire dans le fichier, la méthode renvoie une liste vide. Utilisez-la pour détecter la fin du fichier. En ce qui concerne la taille du tampon, vous pouvez vous attendre à ce que son augmentation puisse améliorer les performances d’entrée, mais il n’y a pas de règle d’or pour cela, essayez de trouver vous-même les valeurs optimales. Regardez le code ci-dessous. Nous l’avons modifié pour vous montrer comment utiliser readlines(). from os import strerror try: ccnt = lcnt = 0 s = open('text.txt', 'rt') lines = s.readlines(20) while len(lines) != 0: for line in lines: lcnt += 1 for ch in line: print(ch, end='') ccnt += 1 lines = s.readlines(10) s.close() print("\n\nCharacters in file:", ccnt) print("Lines in file: ", lcnt) except IOError as e: print("I/O error occurred:", strerror(e.errno)) Nous avons décidé d’utiliser une mémoire tampon de 15 octets. Ne pensez pas que c’est une recommendation, c’est simplement un choix. Nous avons utilisé une telle valeur pour éviter la situation dans laquelle le premier appel de readlines() consomme l’intégralité du fichier. Nous voulons que la méthode soit forcée de travailler plus en profondeur et de démontrer ses capacités. Il y a deux boucles imbriquées dans le code: l’externe utilise le résultat de readlines() pour itérer à travers elle, tandis que l’interne imprime les lignes caractère par caractère. 323 Année académique 2024-2025 16.6.4 Traitement des fichiers texte : on s’accroche ! Le dernier exemple que nous pouvons vous présenter montre un trait très intéressant de l’objet retourné par la fonction open() en mode texte. Ok, on ne sait toujours pas ce qu’est un objet mais ce n’est pas grave, par contre la phrase suivante, nous pensons qu’elle peut vous surprendre : l’objet est une instance de la classe itérable. Étrange? Incompréhensible ? Pas du tout. Utilisable? Oui, absolument. Le protocole d’itération défini pour l’objet fichier est très simple, sa méthode(sa fonction dans notre langage actuel) __next__ renvoie simplement la ligne suivante lue à partir du fichier. De plus, vous pouvez vous attendre à ce que l’objet appelle automatiquement close() lorsque l’une des lectures de fichier atteint la fin du fichier. Regardez le code ci-dessous et voyez à quel point le code est simple et clair. Même sans connaître l’objet, on peut le comprendre et l’appliquer. from os import strerror try: ccnt = lcnt = 0 for line in open('text.txt', 'rt'): lcnt += 1 for ch in line: print(ch, end='') ccnt += 1 print("\n\nCharacters in file:", ccnt) print("Lines in file: ", lcnt) except IOError as e: print("I/O error occurred: ", strerror(e.errno)) 16.6.5 Traitement des fichiers texte : write() L’écriture de fichiers texte semble être plus simple, car il existe en fait qu’une méthode qui peut être utilisée pour effectuer cette tâche. La méthode est nommée write() et elle n’attend qu’un seul argument, une chaîne qui sera transférée vers un fichier ouvert (n’oubliez pas, le mode d’ouverture doit refléter la façon dont les données sont transférées, écrire dans fichier ouvert en mode lecture ne réussira pas). Aucun caractère de nouvelle ligne n’est ajouté à l’argument de write(), vous devez donc l’ajouter vous-même si vous souhaitez que le fichier soit rempli avec un certain nombre de lignes. L’exemple montre un code très simple qui crée un fichier nommé newtext.txt (note: le mode ouvert w garantit que le fichier sera créé à partir de zéro, même s’il existe et contient des données) puis y met dix lignes. La chaîne à enregistrer se compose du mot ligne, suivi du numéro de ligne. Nous avons décidé d’écrire le contenu de la chaîne caractère par caractère (cela se fait par la boucle for interne) mais vous n’êtes pas obligé de le faire de cette façon. 324 Année académique 2024-2025 Nous voulions juste vous montrer que write() est capable de fonctionner sur des caractères uniques. Le code crée un fichier rempli avec le texte suivant : Ligne #1 Ligne #2 Ligne #3 Ligne #4 Ligne #5 Ligne #6 Ligne #7 Ligne #8 Ligne #9 Ligne #10 from os import strerror try: fo = open('newtext.txt', 'wt') # A new file is created. for i in range(10): s = "ligne #" + str(i+1) + "\n" for ch in s: fo.write(ch) fo.close() except IOError as e: print("I/O error occurred: ", strerror(e.errno)) 16.6.6 Traitement des fichiers texte : Avec des lignes entières Mr ? Regardez l’exemple. Nous avons modifié le code précédent pour écrire des lignes entières dans le fichier texte. Le contenu du fichier nouvellement créé est le même. Remarque: vous pouvez utiliser la même méthode pour écrire dans le flux stderr, mais n’essayez pas de l’ouvrir, car il est toujours ouvert implicitement. Par exemple, si vous souhaitez envoyer une chaîne de message à stderr pour le distinguer de la sortie normale du programme, elle peut ressembler à ceci : from os import strerror try: fo = open('newtext.txt', 'wt') for i in range(10): fo.write("line #" + str(i+1) + "\n") fo.close() except IOError as e: print("I/O error occurred: ", strerror(e.errno)) 325 Année académique 2024-2025 16.6.7 Exercices 1. Temps estimé 30-60 minutes Objectifs améliorer les compétences de l’élève dans l’utilisation des dossiers (lecture) utiliser des collectes de données pour compter de nombreuses données. Scénario Un fichier texte contient du texte (rien d’inhabituel) mais nous devons savoir à quelle fréquence chaque lettre apparaît dans le texte. Une telle analyse peut être utile en cryptographie par exemple, nous voulons ici pouvoir le faire en référence à l’alphabet latin. Votre tâche consiste à écrire un programme qui: Demander à l’utilisateur le nom du fichier d’entrée ; Lire le fichier (si possible) et compter toutes les lettres latines (les lettres minuscules et majuscules sont traitées de manière égale) Imprimer un histogramme simple par ordre alphabétique (seuls les nombres non nuls doivent être présentés) Créez un fichier de test pour votre code et vérifiez si votre histogramme contient des résultats valides. En supposant que le fichier de test ne contient qu’une seule ligne remplie par : abc Le résultat attendu devrait être le suivant : a -> 1 b -> 1 C -> 1 Conseil : Nous pensons qu’un dictionnaire est un moyen de collecte de données parfait pour stocker les valeurs. Les lettres peuvent être des clés tandis que les compteurs peuvent être les valeurs. 326 Année académique 2024-2025 2. Temps estimé 15-30 minutes Objectifs améliorer les compétences de l’élève dans l’utilisation de fichiers (lecture/écriture) Utilisation de lambdas pour modifier l’ordre de tri. Scénario. Le code précédent doit être amélioré. C’est correct, mais il faut que ce soit mieux. Votre tâche consiste à apporter quelques modifications, qui génèrent les résultats suivants: L’histogramme de sortie sera trié en fonction de la fréquence des caractères (le plus grand compteur doit être présenté en premier) L’histogramme doit être envoyé à un fichier portant le même nom que celui d’entrée, mais avec le suffixe '.hist' (il doit être concaténé au nom d’origine) En supposant que le fichier d’entrée ne contient qu’une seule ligne remplie par : cBabAa Le résultat attendu devrait être le suivant : a -> 3 b -> 2 c -> 1 Conseil : Utilisez un lambda pour modifier l’ordre de tri. 327 Année académique 2024-2025 Tentons un exo un peu fou …. 3 Temps estimé 30-90 minutes Objectifs améliorer les compétences de l’élève dans l’utilisation de fichiers (lecture) perfectionner les capacités de l’élève à définir et à utiliser des exceptions et des dictionnaires auto-définis. Scénario Le professeur MicMic dirige des cours avec les étudiants et prend régulièrement des notes dans un fichier texte sur eux. Chaque ligne du fichier contient trois éléments : le prénom de l’élève, le nom de famille de l’élève et le nombre de points que l’élève a reçus lors de certains cours. Les éléments sont séparés par des espaces blancs. Chaque étudiant peut apparaître plus d’une fois dans le dossier du professeur. Le fichier peut se présenter comme suit : John Tuche 5 Anna Boule 4.5 John Tuche 2 Anna Boule 11 Jean Petard 1,5 Votre tâche consiste à écrire un programme qui: Demande à l’utilisateur le nom de fichier du professeur; Lit le contenu du fichier et compte la somme des points reçus pour chaque élève; Imprime un rapport simple (mais trié), comme celui-ci : Luca Laporta 1,5 Nathan Vinetot 15,5 Thibaud Danneau 7.0 Note: votre programme doit être entièrement protégé contre toutes les défaillances possibles : l’inexistence du fichier, le vide du fichier ou toute défaillance des données d’entrée ; rencontrer une erreur de données devrait entraîner l’arrêt immédiat du programme, et l’erreur devra être présentée à l’utilisateur; Conseil : Utilisez un dictionnaire pour stocker les données des élèves. 328 Année académique 2024-2025 16.6.8 Résumé 1. Pour lire le contenu d’un fichier, les méthodes de flux suivantes peuvent être utilisées : read(number) : lit les nombres de caractères/octets du fichier et les renvoie sous forme de chaîne ; est capable de lire le fichier entier à la fois; readline() : lit une seule ligne du fichier texte ; readlines(number) : lit les lignes numériques du fichier texte; est capable de lire toutes les lignes à la fois; 2. Pour écrire du nouveau contenu dans un fichier, les méthodes de flux suivantes peuvent être utilisées : write(string) – écrit une chaîne dans un fichier texte ; 3. La méthode open() retourne un objet itérable qui peut être utilisé pour itérer toutes les lignes du fichier à l’intérieur d’une boucle for. Par exemple: for line in open("file", "rt"): print(line, end='') Le code copie le contenu du fichier dans la console, ligne par ligne. Remarque : le flux se ferme automatiquement lorsqu’il atteint la fin du fichier. Exercice 1 Qu’attendons-nous de la méthode readlines() lorsque le flux est associé à un fichier vide ? Exercice 2 Quel est l’objectif du code suivant ? pour la ligne dans open(« file », « rt »): Pour le char en ligne : Si char.lower() n’est pas dans « aeiouy »: print(char, end='') 329

Use Quizgecko on...
Browser
Browser