Programmation Python/Fichiers

Jusqu'à présent, les programmes que nous avons réalisés ne traitaient qu'un très petit nombre de données. Nous pouvions donc à chaque fois inclure ces données dans le corps du programme lui-même (par exemple dans une liste). Cette façon de procéder devient cependant tout à fait inadéquate lorsque l'on souhaite traiter une quantité d'information plus importante.

Utilité des fichiers

modifier

Imaginons par exemple que nous voulons écrire un petit programme exerciseur qui fasse apparaître à l'écran des questions à choix multiple, avec traitement automatique des réponses de l'utilisateur. Comment allons-nous mémoriser le texte des questions elles-mêmes ?

L'idée la plus simple consiste à placer chacun de ces textes dans une variable, en début de programme, avec des instructions d'affectation du genre :

a = "Quelle est la capitale du Guatémala ?"
b = "Qui a succédé à Henri IV ?"
c = "Combien font 26 × 43 ?"
	etc.

Cette idée est malheureusement beaucoup trop simpliste. Tout va se compliquer en effet lorsque nous essayerons d'élaborer la suite du programme, c'est-à-dire les instructions qui devront servir à sélectionner au hasard l'une ou l'autre de ces questions pour les présenter à l'utilisateur. Employer par exemple une longue suite d'instructions if ... elif ... elif ... comme dans l'exemple ci-dessous n'est certainement pas la bonne solution (ce serait d'ailleurs bien pénible à écrire : n'oubliez pas que nous souhaitons traiter un grand nombre de questions !) :

if choix == 1:
    selection = a
elif choix == 2:
    selection = b
elif choix == 3:
    selection = c
	... etc.

La situation se présente déjà beaucoup mieux si nous faisons appel à une liste :

liste = ["Qui a vaincu Napoléon à Waterloo ?",
         "Comment traduit-on 'informatique' en anglais ?",
         "Quelle est la formule chimique du méthane ?", ... etc ...]

On peut en effet extraire n'importe quel élément de cette liste à l'aide de son indice. Exemple :

print liste[2]        ===>  "Quelle est la formule chimique du méthane ?"

(rappel : l'indiçage commence à partir de zéro)

Même si cette façon de procéder est déjà nettement meilleure que la précédente, nous sommes toujours confrontés à plusieurs problèmes gênants :

  • La lisibilité du programme va se détériorer très vite lorsque le nombre de questions deviendra important. En corollaire, nous accroîtrons la probabilité d'insérer l'une ou l'autre erreur de syntaxe dans la définition de cette longue liste. De telles erreurs seront bien difficiles à débusquer.
  • L'ajout de nouvelles questions, ou la modification de certaines d'entre elles, imposeront à chaque fois de rouvrir le code source du programme. En corollaire, il deviendra malaisé de retravailler ce même code source, puisqu'il comportera de nombreuses lignes de données encombrantes.
  • L'échange de données avec d'autres programmes (peut-être écrits dans d'autres langages) est tout simplement impossible, puisque ces données font partie du programme lui-même.

Cette dernière remarque nous suggère la direction à prendre : il est temps que nous apprenions à séparer les données, et les programmes qui les traitent, dans des fichiers différents.

Pour que cela devienne possible, nous devrons doter nos programmes de divers mécanismes permettant de créer des fichiers, d'y envoyer des données et de les récupérer par après.

Les langages de programmation proposent des jeux d'instructions plus ou moins sophistiqués pour effectuer ces tâches. Lorsque les quantités de données deviennent très importantes, il devient d'ailleurs rapidement nécessaire de structurer les relations entre ces données, et l'on doit alors élaborer des systèmes appelés bases de données relationnelles, dont la gestion peut s'avérer très complexe. Ce sera là l'affaire de logiciels très spécialisés tels que Oracle, IBM DB, Adabas, PostgreSQL, MySQL, etc. Python est parfaitement capable de dialoguer avec ces systèmes, mais nous laisserons cela pour un peu plus tard (voir chapitre Gestion d'une base de données).

Nos ambitions présentes sont plus modestes. Nos données ne se comptent pas encore par centaines de milliers, aussi nous pouvons nous contenter de mécanismes simples pour les enregistrer dans un fichier de taille moyenne, et les en extraire ensuite.

Travailler avec des fichiers

modifier

L'utilisation d'un fichier ressemble beaucoup à l'utilisation d'un livre. Pour utiliser un livre, vous devez d'abord le trouver (à l'aide de son titre), puis l'ouvrir. Lorsque vous avez fini de l'utiliser, vous le refermez. Tant qu'il est ouvert, vous pouvez y lire des informations diverses, et vous pouvez aussi y écrire des annotations, mais généralement vous ne faites pas les deux à la fois. Dans tous les cas, vous pouvez vous situer à l'intérieur du livre, notamment en vous aidant des numéros de pages. Vous lisez la plupart des livres en suivant l'ordre normal des pages, mais vous pouvez aussi décider de consulter n'importe quel paragraphe dans le désordre.

Tout ce que nous venons de dire des livres s'applique aussi aux fichiers informatiques. Un fichier se compose de données enregistrées sur votre disque dur, sur une disquette ou sur un CD-ROM. Vous y accédez grâce à son nom (lequel peut inclure aussi un nom de répertoire). Vous pouvez toujours considérer le contenu d'un fichier comme une suite de caractères, ce qui signifie que vous pouvez traiter ce contenu, ou une partie quelconque de celui-ci, à l'aide des fonctions servant à traiter les chaînes de caractères.

Noms de fichiers - Répertoire courant

modifier

Pour simplifier les explications qui vont suivre, nous indiquerons seulement des noms simples pour les fichiers que nous allons manipuler. Si vous procédez ainsi dans vos exercices, les fichiers en question seront créés et/ou recherchés par Python dans le répertoire courant. Celui-ci est habituellement le répertoire où se trouve le script lui-même, sauf si vous lancez ce script depuis la fenêtre d'un shell IDLE, auquel cas le répertoire courant est défini au lancement de IDLE lui-même (Sous Windows, la définition de ce répertoire fait partie des propriétés de l'icône de lancement).

Si vous travaillez avec IDLE, vous souhaiterez donc certainement forcer Python à changer son répertoire courant, afin que celui-ci corresponde à vos attentes. Pour ce faire, utilisez les commandes suivantes en début de session. (Nous supposons ici que le répertoire visé est le répertoire /home/jules/exercices. Vous pouvez franchement utiliser cette syntaxe (c'est-à-dire des caractères / et non \ en guise de séparateurs : c'est la convention en vigueur dans le monde Unix). Python effectuera automatiquement les conversions nécessaires, suivant que vous travaillez sous MacOS, Linux, ou Windows.[1]

>>> from os import chdir
>>> chdir("/home/jules/exercices")

La première commande importe la fonction chdir() du module os. Le module os contient toute une série de fonctions permettant de dialoguer avec le système d'exploitation (os = operating system), quel que soit celui-ci.

La seconde commande provoque le changement de répertoire (chdir = change directory)

Notes
  • Vous avez également la possibilité d'insérer ces commandes en début de script, ou encore d'indiquer le chemin d'accès complet dans le nom des fichiers que vous manipulez, mais cela risque peut-être d'alourdir l'écriture de vos programmes.
  • Choisissez de préférence des noms de fichiers courts. Évitez dans toute la mesure du possible les caractères accentués, les espaces et les signes typographiques spéciaux.

Les deux formes d'importation

modifier

Les lignes d'instructions que nous venons d'utiliser sont l'occasion d'expliquer un mécanisme intéressant. Vous savez qu'en complément des fonctions intégrées dans le module de base, Python met à votre disposition une très grande quantité de fonctions plus spécialisées, qui sont regroupées dans des modules. Ainsi vous connaissez déjà fort bien le module math et le module Tkinter.

Pour utiliser les fonctions d'un module, il suffit de les importer. Mais cela peut se faire de deux manières différentes, comme nous allons le voir ci-dessous. Chacune des deux méthodes présente des avantages et des inconvénients.

Voici un exemple de la première méthode :

>>>>>> import os
>>> rep_cour = os.getcwd()
>>> print rep_cour
C:\Python22\essais

La première ligne de cet exemple importe l'intégralité du module os, lequel contient de nombreuses fonctions intéressantes pour l'accès au système d'exploitation. La seconde ligne utilise la fonction getcwd() du module os[2]. Comme vous pouvez le constater, la fonction getcwd() renvoie le nom du répertoire courant (getcwd = get current working directory).

Par comparaison, voici un exemple similaire utilisant la seconde méthode d'importation :

>>> from os import getcwd
>>> rep_cour = getcwd() 
>>> print rep_cour
C:\Python22\essais

Dans ce nouvel exemple, nous n'avons importé du module os que la fonction getcwd() seule. Importée de cette manière, la fonction s'intègre à notre propre code comme si nous l'avions écrite nous-mêmes. Dans les lignes où nous l'utilisons, il n'est pas nécessaire de rappeler qu'elle fait partie du module os.

Nous pouvons de la même manière importer plusieurs fonctions du même module :

>>> from math import sqrt, pi, sin, cos
>>> print pi
3.14159265359
>>> print sqrt(5)    # racine carrée de 5
2.2360679775
>>> print sin(pi/6)  # sinus d'un angle de 30°
0.5

Nous pouvons même importer toutes les fonctions d'un module, comme dans :

from Tkinter import *

Cette méthode d'importation présente l'avantage d'alléger l'écriture du code. Elle présente l'inconvénient (surtout dans sa dernière forme, celle qui importe toutes les fonctions d'un module) d'encombrer l'espace de noms courant. Il se pourrait alors que certaines fonctions importées aient le même nom que celui d'une variable définie par vous-même, ou encore le même nom qu'une fonction importée depuis un autre module. (Si cela se produit, l'un des deux noms en conflit n'est évidemment plus accessible).

Dans les programmes d'une certaine importance, qui font appel à un grand nombre de modules d'origines diverses, il sera donc toujours préférable de privilégier plutôt la première méthode, c'est-à-dire celle qui utilise des noms pleinement qualifiés.

On fait généralement exception à cette règle dans le cas particulier du module Tkinter, parce que les fonctions qu'il contient sont très sollicitées (dès lors que l'on décide d'utiliser ce module).

Écriture séquentielle dans un fichier

modifier

Sous Python, l'accès aux fichiers est assuré par l'intermédiaire d'un objet-fichier que l'on crée à l'aide de la fonction interne open(). Après avoir appelé cette fonction, vous pouvez lire et écrire dans le fichier en utilisant les méthodes spécifiques de cet objet-fichier.

L'exemple ci-dessous vous montre comment ouvrir un fichier « en écriture », y enregistrer deux chaînes de caractères, puis le refermer. Notez bien que si le fichier n'existe pas encore, il sera créé automatiquement. Par contre, si le nom utilisé concerne un fichier préexistant qui contient déjà des données, les caractères que vous y enregistrerez viendront s'ajouter à la suite de ceux qui s'y trouvent déjà. Vous pouvez faire tout cet exercice directement à la ligne de commande :

file_path = 'mon_dossier/mon_fichier'
if (os.path.isfile(file_path)):
    obFichier = open(file_path,'a')
    obFichier.write('Bonjour, fichier !')
    obFichier.write("Quel beau temps, aujourd'hui !")
    obFichier.close()
Notes
  • La première ligne crée l'objet-fichier « obFichier », lequel fait référence à un fichier véritable (sur disque ou disquette) dont le nom sera Monfichier. Ne confondez pas le nom de fichier avec le nom de l'objet-fichier qui y donne accès. À la suite de cet exercice, vous pouvez vérifier qu'il s'est bien créé sur votre système (dans le répertoire courant) un fichier dont le nom est Monfichier (et vous pouvez en visualiser le contenu à l'aide d'un éditeur quelconque).
  • La fonction open() attend deux arguments, qui doivent être des chaînes de caractères. Le premier argument est le nom du fichier à ouvrir, et le second est le mode d'ouverture. a indique qu'il faut ouvrir ce fichier en mode « ajout » (append), ce qui signifie que les données à enregistrer doivent être ajoutées à la fin du fichier, à la suite de celles qui s'y trouvent éventuellement déjà. Nous aurions pu utiliser aussi le mode w (pour write), mais lorsqu'on utilise ce mode, Python crée toujours un nouveau fichier (vide), et l'écriture des données commence à partir du début de ce nouveau fichier. S'il existe déjà un fichier de même nom, celui-ci est effacé au préalable.
    Exemple : file = open(u'fichier_à_lire_puis_compléter','r+b')
  • La méthode write() réalise l'écriture proprement dite. Les données à écrire doivent être fournies en argument. Ces données sont enregistrées dans le fichier les unes à la suite des autres (c'est la raison pour laquelle on parle de fichier à accès séquentiel). Chaque nouvel appel de write() continue l'écriture à la suite de ce qui est déjà enregistré.
  • La méthode close() referme le fichier. Celui-ci est désormais disponible pour tout usage.

Lecture séquentielle d'un fichier

modifier

Vous allez maintenant rouvrir le fichier, mais cette fois « en lecture », de manière à pouvoir y relire les informations que vous avez enregistrées dans l'étape précédente :

>>> ofi = open('Monfichier', 'r')
>>> t = ofi.read()
>>> print t
Bonjour, fichier ! Quel beau temps, aujourd'hui !
>>> ofi.close()

Comme on pouvait s'y attendre, la méthode read() lit les données présentes dans le fichier et les transfère dans une variable de type « chaîne » (string) . Si on utilise cette méthode sans argument, la totalité du fichier est transférée.

Notes
  • Le fichier que nous voulons lire s'appelle Monfichier. L'instruction d'ouverture de fichier devra donc nécessairement faire référence à ce nom-là. Si le fichier n'existe pas, nous obtenons un message d'erreur. Exemple :
    >>> ofi = open('Monficier','r')
    IOError: [Errno 2] No such file or directory: 'Monficier'
    
    Par contre, nous ne sommes tenus à aucune obligation concernant le nom à choisir pour l'objet-fichier. C'est un nom de variable quelconque. Ainsi donc, dans notre première instruction, nous avons choisi de créer un objet-fichier ofi, faisant référence au fichier réel Monfichier, lequel est ouvert en lecture (argument r).
  • Les deux chaînes de caractères que nous avions entrées dans le fichier sont à présent accolées en une seule. C'est normal, puisque nous n'avons fourni aucun caractère de séparation lorsque nous les avons enregistrées.
  • La méthode read() peut également être utilisée avec un argument. Celui-ci indiquera combien de caractères doivent être lus, à partir de la position déjà atteinte dans le fichier :
    >>> ofi = open('Monfichier', 'r')
    >>> t = ofi.read(7)
    >>> print t
    Bonjour
    >>> t = ofi.read(15)
    >>> print t
    , fichier !Quel
    


    S'il ne reste pas assez de caractères au fichier pour satisfaire la demande, la lecture s'arrête tout simplement à la fin du fichier :

    >>> t = ofi.read(1000)
    >>> print t
     beau temps, aujourd'hui !
    

    Si la fin du fichier est déjà atteinte, read() renvoie une chaîne vide :

    >>> t = ofi.read()
    >>> print t
    
    
    >>> ofi.close()
    

Exemple avec une boucle

modifier

Il va de soi que les boucles de programmation s'imposent lorsque l'on doit traiter un fichier dont on ne connaît pas nécessairement le contenu à l'avance. L'idée de base consistera à lire ce fichier morceau par morceau, jusqu'à ce que l'on ait atteint la fin du fichier.

La fonction ci-dessous illustre cette idée. Elle copie l'intégralité d'un fichier, quelle que soit sa taille, en transférant des portions de 50 caractères à la fois :

def copieFichier(source, destination):
    "copie intégrale d'un fichier" 
    fs = open(source, 'r')
    fd = open(destination, 'w')
    while 1:
        txt = fs.read(50)
        if txt =="":
            break
        fd.write(txt)
    fs.close()
    fd.close()
    return

Si vous voulez tester cette fonction, vous devez lui fournir deux arguments : le premier est le nom du fichier original, le second est le nom à donner au fichier qui accueillera la copie. Exemple :

copieFichier('Monfichier','Tonfichier')

Fichiers texte

modifier

Un fichier texte est un fichier qui contient des caractères imprimables et des espaces organisés en lignes successives, ces lignes étant séparées les unes des autres par un caractère spécial non-imprimable appelé « marqueur de fin de ligne »[3].

Il est très facile de traiter ce genre de fichiers sous Python. Les instructions suivantes créent un fichier texte de quatre lignes :

>>> f = open("Fichiertexte", "w")
>>> f.write("Ceci est la ligne un\nVoici la ligne deux\n")
>>> f.write("Voici la ligne trois\nVoici la ligne quatre\n")
>>> f.close()

Notez bien le marqueur de fin de ligne \n inséré dans les chaînes de caractères, aux endroits où l'on souhaite séparer les lignes de texte dans l'enregistrement. Sans ce marqueur, les caractères seraient enregistrés les uns à la suite des autres, comme dans les exemples précédents.

Lors des opérations de lecture, les lignes d'un fichier texte peuvent être extraites séparément les unes des autres. La méthode readline(), par exemple, ne lit qu'une seule ligne à la fois (en incluant le caractère de fin de ligne) :

>>> f = open('Fichiertexte','r')
>>> t = f.readline()
>>> print t
Ceci est la ligne un
>>> print f.readline()
Voici la ligne deux


La méthode readlines() transfère toutes les lignes restantes dans une liste de chaînes :

>>> t = f.readlines()
>>> print t
['Voici la ligne trois\012', 'Voici la ligne quatre\012']
>>> f.close()
Remarques
  • La liste apparaît ci-dessus en format brut, avec des apostrophes pour délimiter les chaînes, et les caractères spéciaux sous forme de codes numériques. Vous pourrez bien évidemment parcourir cette liste (à l'aide d'une boucle while, par exemple) pour en extraire les chaînes individuelles.
  • La méthode readlines() permet donc de lire l'intégralité d'un fichier en une instruction seulement. Cela n'est possible toutefois que si le fichier à lire n'est pas trop gros (Puisqu'il est copié intégralement dans une variable, c'est-à-dire dans la mémoire vive de l'ordinateur, il faut que la taille de celle-ci soit suffisante). Si vous devez traiter de gros fichiers, utilisez plutôt la méthode readline() dans une boucle, comme le montrera l'exemple de la page suivante.
  • Notez bien que readline() est une méthode qui renvoie une chaîne de caractères, alors que la méthode readlines() renvoie une liste. À la fin du fichier, readline() renvoie une chaîne vide, tandis que readlines() renvoie une liste vide.

Le script qui suit vous montre comment créer une fonction destinée à effectuer un certain traitement sur un fichier texte. En l'occurrence, il s'agit ici de recopier un fichier texte en omettant toutes les lignes qui commencent par un caractère # :

def filtre(source,destination):
    "recopier un fichier en éliminant les lignes de remarques"
    fs = open(source, 'r')
    fd = open(destination, 'w')
    while 1:
        txt = fs.readline()
        if txt =='':
            break
        if txt[0] != '#':
            fd.write(txt)
    fs.close()
    fd.close()
    return

Pour appeler cette fonction, vous devez utiliser deux arguments : le nom du fichier original, et le nom du fichier destiné à recevoir la copie filtrée. Exemple :

filtre('test.txt', 'test_f.txt')

Enregistrement et restitution de variables diverses

modifier

L'argument de la méthode write() doit être une chaîne de caractères. Avec ce que nous avons appris jusqu'à présent, nous ne pouvons donc enregistrer d'autres types de valeurs qu'en les transformant d'abord en chaînes de caractères.

Nous pouvons réaliser cela à l'aide de la fonction intégrée str() :

>>> x = 52
>>> f.write(str(x))

Si nous enregistrons les valeurs numériques en les transformant d'abord en chaînes de caractères, nous risquons de ne plus pouvoir les re-transformer correctement en valeurs numériques lorsque nous allons relire le fichier. Exemple :

>>> a = 5
>>> b = 2.83
>>> c = 67
>>> f = open('Monfichier', 'w')
>>> f.write(str(a))
>>> f.write(str(b))
>>> f.write(str(c))
>>> f.close()
>>> f = open('Monfichier', 'r')
>>> print(f.read())
52.8367
>>> f.close()

Nous avons enregistré trois valeurs numériques. Mais comment pouvons-nous les distinguer dans la chaîne de caractères résultante, lorsque nous effectuons la lecture du fichier ? C'est impossible ! Rien ne nous indique d'ailleurs qu'il y a là trois valeurs plutôt qu'une seule, ou 2, ou 4,…

Il existe plusieurs solutions à ce genre de problèmes. L'une des meilleures consiste à importer un module Python spécialisé : le module pickle[4]. Voici comment il s'utilise :

>>> import pickle
>>> f = open('Monfichier', 'wb')
>>> pickle.dump(a, f)
>>> pickle.dump(b, f)
>>> pickle.dump(c, f)
>>> f.close()
>>> f = open('Monfichier', 'rb')
>>> t = pickle.load(f)
>>> print(t, type(t))
5 <type 'int'>
>>> t = pickle.load(f)
>>> print(t, type(t))
2.83 <type 'float'>
>>> t = pickle.load(f)
>>> print(t, type(t))
67 <type 'int'>
>>> f.close()

Pour cet exemple, on considère que les variables a, b et c contiennent les mêmes valeurs que dans l'exemple précédent. La fonction dump() du module pickle attend deux arguments : le premier est la variable à enregistrer, le second est l'objet fichier dans lequel on travaille. La fonction pickle.load() effectue le travail inverse, c'est-à-dire la restitution de chaque variable avec son type.

Vous pouvez aisément comprendre ce que font exactement les fonctions du module pickle en effectuant une lecture « classique » du fichier résultant, à l'aide de la méthode read() par exemple.

Lister des fichiers

modifier

La fonction os.listdir() liste les fichiers du dossier courant, ou ceux du dossier en paramètre s'il est mentioné.

Exercices

modifier

Exercices

  1. Écrivez un script qui compte dans un fichier texte quelconque le nombre de lignes contenant des caractères numériques.
  2. Écrivez un script qui compte le nombre de mots contenus dans un fichier texte.
  3. Écrivez un script qui recopie un fichier texte en veillant à ce que chaque ligne commence par une majuscule.
  4. Écrivez un script qui recopie un fichier texte en fusionnant (avec la précédente) les lignes qui ne commencent pas par une majuscule.
  5. Vous disposez d'un fichier contenant des valeurs numériques. Considérez que ces valeurs sont les diamètres d'une série de sphères. Écrivez un script qui utilise les données de ce fichier pour en créer un autre, organisé en lignes de texte qui exprimeront « en clair » les autres caractéristiques de ces sphères (surface de section, surface extérieure et volume), dans des phrases telles que :
    Diam. 46.20 cm Section = 1676.39 cm² Surf. = 6705.54 cm². Vol. = 51632.67 cm³
    Diam. 120.00 cm Section = 11309.73 cm² Surf. = 45238.93 cm². Vol. = 904778.68 cm³
    Diam. 0.03 cm Section = 0.00 cm² Surf. = 0.00 cm². Vol. = 0.00 cm³
    Diam. 13.90 cm Section = 151.75 cm² Surf. = 606.99 cm². Vol. = 1406.19 cm³
    Diam. 88.80 cm Section = 6193.21 cm² Surf. = 24772.84 cm². Vol. = 366638.04 cm³
    etc.
  6. Vous avez à votre disposition un fichier texte dont les lignes représentent des valeurs numériques de type réel, sans exposant (et encodées sous forme de chaînes de caractères).
    Écrivez un script qui recopie ces valeurs dans un autre fichier en les arrondissant de telle sorte que leur partie décimale ne comporte plus qu'un seul chiffre après la virgule, celui-ci ne pouvant être que 0 ou 5 (l'arrondi doit être correct).

Solution

  1. Réfléchissez !
  2. # Comptage du nombre de mots dans un texte
    
    fiSource = raw_input("Nom du fichier à traiter : ")
    fs = open(fiSource, 'r')
    
    n = 0           # variable compteur
    while 1:
        ch = fs.readline()
        if ch == "":
            break
        # conversion de la chaîne lue en une liste de mots :
        li = ch.split()
        # totalisation des mots :
        n = n + len(li)    
    fs.close()
    print("Ce fichier texte contient un total de %s mots" % (n))
    
  3. # Conversion en majuscule du premier caractère de chaque ligne
    
    fiSource = raw_input("Nom du fichier à traiter : ")
    fiDest = raw_input("Nom du fichier destinataire : ")
    fs = open(fiSource, 'r')
    fd = open(fiDest, 'w')
    
    while 1:
        ch = fs.readline()
        if ch == "":
            break
        if ch[0] >= "A" and ch[0] <= "Z":
            # le premier car. est une majuscule. On passe.
            pass
        else:
            # Reconstruction de la chaîne:
            pc = ch[0].upper()      # Premier caractère converti
            rc = ch[1:]             # toute le reste de la chaîne  
            ch = pc + rc            # fusion
            # variante utilisant une méthode encore plus intégrée :
            # ch = ch.capitalize()
        # Transcription :
        fd.write(ch)
    
    fd.close()
    fs.close()
    
  4. # Fusion de lignes pour former des phrases
    
    fiSource = raw_input("Nom du fichier à traiter : ")
    fiDest = raw_input("Nom du fichier destinataire : ")
    fs = open(fiSource, 'r')
    fd = open(fiDest, 'w')
    
    
    # On lit d'abord la première ligne :
    ch1 = fs.readline()
    # On lit ensuite les suivantes, en les fusionnant si nécessaire :
    while 1:
        ch2 = fs.readline()
        if ch2 == "":
            break
        # Si la chaîne lue commence par une majuscule, on transcrit
        # la précédente dans le fichier destinataire, et on la
        # remplace par celle que l'on vient de lire :
        if ch2[0] >= "A" and ch2[0] <= "Z":
            fd.write(ch1)
            ch1 = ch2
        # Sinon, on la fusionne avec la précédente :
        else:
            ch1 = ch1[:-1] + " " + ch2
            # (veiller à enlever de ch1 le caractère de fin de ligne)
            
    fd.write(ch1)        # ne pas oublier de transcrire la dernière !
    fd.close()
    fs.close()
    
  5. # Caractéristiques de sphères :
    # Le fichier de départ est un fichier <texte> dont chaque ligne contient
    # un nombre réel (encodé sous la forme d'une chaîne de caractères)    
    
    from math import pi
    
    def caractSphere(d):
        "renvoie les caractéristiques d'une sphère de diamètre d"
        d = float(d)        # conversion de l'argument (=chaîne) en réel
        r = d/2             # rayon
        ss = pi*r**2        # surface de section
        se = 4*pi*r**2      # surface extérieure
        v = 4./3*pi*r**3    # volume  (! la 1e division doit être réelle !)
        # Le marqueur de conversion %8.2f utilisé ci-dessous formate le nombre
        # affiché de manière à occuper 8 caractères au total, en arrondissant
        # de manière à conserver deux chiffres après la virgule : 
        ch = "Diam. %6.2f cm Section = %8.2f cm² " % (d, ss)
        ch = ch +"Surf. = %8.2f cm². Vol. = %9.2f cm³" % (se, v)
        return ch
    
    fiSource = raw_input("Nom du fichier à traiter : ")
    fiDest = raw_input("Nom du fichier destinataire : ")
    fs = open(fiSource, 'r')
    fd = open(fiDest, 'w')
    while 1:
        diam = fs.readline()
        if diam == "" or diam == "\n":
            break
        fd.write(caractSphere(diam) + "\n")         # enregistrement
    fd.close()
    fs.close()
    
  6. # Mise en forme de données numériques
    # Le fichier traité est un fichier <texte> dont chaque ligne contient un nombre
    # réel (sans exposants et encodé sous la forme d'une chaîne de caractères)    
    
    def arrondir(reel):
        "représentation arrondie à .0 ou .5 d'un nombre réel"
        ent = int(reel)             # partie entière du nombre
        fra = reel - ent            # partie fractionnaire
        if fra < .25 :
            fra = 0
        elif fra < .75 :
            fra = .5
        else:
            fra = 1
        return ent + fra    
    
    fiSource = raw_input("Nom du fichier à traiter : ")
    fiDest = raw_input("Nom du fichier destinataire : ")
    fs = open(fiSource, 'r')
    fd = open(fiDest, 'w')
    while 1:
        ligne = fs.readline()
        if ligne == "" or ligne == "\n":
            break
        n = arrondir(float(ligne))      # conversion en <float>, puis arrondi
        fd.write(str(n) + "\n")         # enregistrement
    
    fd.close()
    fs.close()
    

Exercices

  1. Écrivez un script qui permette de créer et de relire aisément un fichier texte. Votre programme demandera d'abord à l'utilisateur d'entrer le nom du fichier. Ensuite il lui proposera le choix, soit d'enregistrer de nouvelles lignes de texte, soit d'afficher le contenu du fichier.
    L'utilisateur devra pouvoir entrer ses lignes de texte successives en utilisant simplement la touche <Enter> pour les séparer les unes des autres. Pour terminer les entrées, il lui suffira d'entrer une ligne vide (c'est-à-dire utiliser la touche <Enter> seule).
    L'affichage du contenu devra montrer les lignes du fichier séparées les unes des autres de la manière la plus naturelle (les codes de fin de ligne ne doivent pas apparaître).
  2. Considérons que vous avez à votre disposition un fichier texte contenant des phrases de différentes longueurs. Écrivez un script qui recherche et affiche la phrase la plus longue.
  3. Écrivez un script qui génère automatiquement un fichier texte contenant les tables de multiplication de 2 à 30 (chacune d'entre elles incluant 20 termes seulement).
  4. Écrivez un script qui recopie un fichier texte en triplant tous les espaces entre les mots.
  5. Vous avez à votre disposition un fichier texte dont chaque ligne est la représentation d'une valeur numérique de type réel (mais sans exposants). Par exemple :
    14.896
    7894.6
    123.278
    etc.
    Écrivez un script qui recopie ces valeurs dans un autre fichier en les arrondissant en nombres entiers (l'arrondi doit être correct).
  6. Écrivez un script qui compare les contenus de deux fichiers et signale la première différence rencontrée.
  7. À partir de deux fichiers préexistants A et B, construisez un fichier C qui contienne alternativement un élément de A, un élément de B, un élément de A, ... et ainsi de suite jusqu'à atteindre la fin de l'un des deux fichiers originaux. Complétez ensuite C avec les éléments restant sur l'autre.
  8. Écrivez un script qui permette d'encoder un fichier texte dont les lignes contiendront chacune les noms, prénom, adresse, code postal et n° de téléphone de différentes personnes (considérez par exemple qu'il s'agit des membres d'un club)
  9. Écrivez un script qui recopie le fichier utilisé dans l'exercice précédent, en y ajoutant la date de naissance et le sexe des personnes (l'ordinateur devra afficher les lignes une par une, et demander à l'utilisateur d'entrer pour chacune les données complémentaires).
  10. Considérons que vous avez fait les exercices précédents et que vous disposez à présent d'un fichier contenant les coordonnées d'un certain nombre de personnes. Écrivez un script qui permette d'extraire de ce fichier les lignes qui correspondent à un code postal bien déterminé.
  11. Modifiez le script de l'exercice précédent, de manière à retrouver les lignes correspondant à des prénoms dont la première lettre est située entre F et M (inclus) dans l'alphabet.
  12. Écrivez des fonctions qui effectuent le même travail que celles du module "pickle". Ces fonctions doivent permettre l'enregistrement de variables diverses dans un fichier texte, en les accompagnant systématiquement d'informations concernant leur format exact.

Solution

  1. #(éditeur simple, pour lire et écrire dans un fichier 'texte') :
    def sansDC(ch):
        "cette fonction renvoie la chaîne ch amputée de son dernier caractère"
        nouv = ""
        i, j = 0, len(ch) -1        
        while i < j:
            nouv = nouv + ch[i]
            i = i + 1
        return nouv    
    
    def ecrireDansFichier():
        of = open(nomF, 'a')
        while 1:
            ligne = raw_input("entrez une ligne de texte (ou <Enter>) : ")
            if ligne == '':
                break
            else:
                of.write(ligne + '\n')
        of.close()
    
    def lireDansFichier():
        of = open(nomF, 'r')
        while 1:
            ligne = of.readline()
            if ligne == "":
                break
            # afficher en omettant le dernier caractère (= fin de ligne) :
            print sansDC(ligne)
        of.close()        
        
    nomF = raw_input('Nom du fichier à traiter : ')
    choix = raw_input('Entrez "e" pour écrire, "c" pour consulter les données : ')
    
    if choix =='e':    
        ecrireDansFichier()
    else:
        lireDansFichier()
    
  2. Réfléchissez !
  3. #(génération des tables de multiplication de 2 à 30) :
    def tableMulti(n):
        # Fonction générant la table de multiplication par n (20 termes)
        # La table sera renvoyée sous forme d'une chaîne de caractères :
        i, ch = 0, ""
        while i < 20:        
            i = i + 1
            ch = ch + str(i * n) + " "
        return ch
    
    NomF = raw_input("Nom du fichier à créer : ")
    fichier = open(NomF, 'w')
    
    # Génération des tables de 2 à 30 :
    table = 2
    while table < 31:
        fichier.write(tableMulti(table) + '\n')
        table = table + 1
    fichier.close()
    Exercice 9.4 :
    # Triplement des espaces dans un fichier texte.
    # Ce script montre également comment modifier le contenu d'un fichier
    # en le transférant d'abord tout entier dans une liste, puis en
    # réenregistrant celle-ci après modifications
    
    def triplerEspaces(ch):
        "fonction qui triple les espaces entre mots dans la chaîne ch"
        i, nouv = 0, ""
        while i < len(ch):
            if ch[i] == " ":
                nouv = nouv + "   "
            else:
                nouv = nouv + ch[i]
            i = i +1    
        return nouv
    
    NomF = raw_input("Nom du fichier : ")
    fichier = open(NomF, 'r+')              # 'r+' = mode read/write
    lignes = fichier.readlines()            # lire toutes les lignes
    
    n=0
    while n < len(lignes):
        lignes[n] = triplerEspaces(lignes[n])
        n =n+1
        
    fichier.seek(0)                         # retour au début du fichier
    fichier.writelines(lignes)              # réenregistrement
    fichier.close()
    
  4. Réfléchissez !
  5. # Mise en forme de données numériques.
    # Le fichier traité est un fichier texte dont chaque ligne contient un nombre
    # réel (sans exposants et encodé sous la forme d'une chaîne de caractères)    
    
    def valArrondie(ch):
        "représentation arrondie du nombre présenté dans la chaîne ch"
        f = float(ch)       # conversion de la chaîne en un nombre réel
        e = int(f + .5)     # conversion en entier (On ajoute d'abord
                            # 0.5 au réel pour l'arrondir correctement)
        return str(e)       # reconversion en chaîne de caractères
         
    fiSource = raw_input("Nom du fichier à traiter : ")
    fiDest = raw_input("Nom du fichier destinataire : ")
    fs = open(fiSource, 'r')
    fd = open(fiDest, 'w')
    
    while 1:
        ligne = fs.readline()       # lecture d'une ligne du fichier
        if ligne == "" or ligne == "\n":
            break
        ligne = valArrondie(ligne)
        fd.write(ligne +"\n")
        
    fd.close()
    fs.close()
    Exercice 9.6 :
    # Comparaison de deux fichiers, caractère par caractère :
    
    fich1 = raw_input("Nom du premier fichier : ")
    fich2 = raw_input("Nom du second fichier : ")
    fi1 = open(fich1, 'r')
    fi2 = open(fich2, 'r')
    
    c, f = 0, 0                 # compteur de caractères et "drapeau" 
    while 1:
        c = c + 1
        car1 = fi1.read(1)      # lecture d'un caractère dans chacun
        car2 = fi2.read(1)      # des deux fichiers
        if car1 =="" or car2 =="":
            break
        if car1 != car2 :
            f = 1
            break               # différence trouvée
    
    fi1.close()
    fi2.close()
    
    print "Ces 2 fichiers",
    if f ==1:
        print "diffèrent à partir du caractère n°", c
    else:
        print "sont identiques."
    
  6. Réfléchissez !
  7. # Combinaison de deux fichiers texte pour en faire un nouveau
    
    fichA = raw_input("Nom du premier fichier : ")
    fichB = raw_input("Nom du second fichier : ")
    fichC = raw_input("Nom du fichier destinataire : ")
    fiA = open(fichA, 'r')
    fiB = open(fichB, 'r')
    fiC = open(fichC, 'w')
    
    while 1:
        ligneA = fiA.readline()    
        ligneB = fiB.readline()
        if ligneA =="" and ligneB =="":
            break               # On est arrivé à la fin des 2 fichiers
        if ligneA != "":
            fiC.write(ligneA)
        if ligneB != "":    
            fiC.write(ligneB)
    
    fiA.close()
    fiB.close()
    fiC.close()
    
  8. # Enregistrer les coordonnées des membres d'un club
    
    def encodage():
        "renvoie la liste des valeurs entrées, ou une liste vide"
        print "*** Veuillez entrer les données (ou <Enter> pour terminer) :"
        while 1:
            nom = raw_input("Nom : ")
            if nom == "":
                return []
            prenom = raw_input("Prénom : ")
            rueNum = raw_input("Adresse (N° et rue) : ")
            cPost = raw_input("Code postal : ")
            local = raw_input("Localité : ")
            tel = raw_input("N° de téléphone : ")
            print nom, prenom, rueNum, cPost, local, tel
            ver = raw_input("Entrez <Enter> si c'est correct, sinon <n> ")
            if ver == "":
                break
        return [nom, prenom, rueNum, cPost, local, tel]
    
    def enregistrer(liste):
        "enregistre les données de la liste en les séparant par des <#>"
        i = 0
        while i < len(liste):
            of.write(liste[i] + "#")
            i = i + 1
        of.write("\n")              # caractère de fin de ligne    
        
    nomF = raw_input('Nom du fichier destinataire : ')
    of = open(nomF, 'a')
    while 1:
        tt = encodage()
        if tt == []:
            break
        enregistrer(tt)
    
    of.close()
    
  9. # Ajouter des informations dans le fichier du club
    
    def traduire(ch):
        "convertir une ligne du fichier source en liste de données"
        dn = ""                 # chaîne temporaire pour extraire les données  
        tt = []                 # la liste à produire
        i = 0
        while i < len(ch):
            if ch[i] == "#":
                tt.append(dn)   # on ajoute la donnée à la liste, et   
                dn =""          # on réinitialise la chaine temporaire
            else:    
                dn = dn + ch[i]
            i = i + 1
        return tt    
        
    def encodage(tt):
        "renvoyer la liste tt, complétée avec la date de naissance et le sexe"
        print "*** Veuillez entrer les données (ou <Enter> pour terminer) :"
        # Affichage des données déjà présentes dans la liste :
        i = 0
        while i < len(tt):
            print tt[i],
            i = i +1
        print
        while 1:
            daNai = raw_input("Date de naissance : ")
            sexe = raw_input("Sexe (m ou f) : ")
            print daNai, sexe
            ver = raw_input("Entrez <Enter> si c'est correct, sinon <n> ")
            if ver == "":
                break
        tt.append(daNai)
        tt.append(sexe)
        return tt
    
    def enregistrer(tt):
        "enregistrer les données de la liste tt en les séparant par des <#>"
        i = 0
        while i < len(tt):
            fd.write(tt[i] + "#")
            i = i + 1
        fd.write("\n")          # caractère de fin de ligne
    
    fSource = raw_input('Nom du fichier source : ')
    fDest = raw_input('Nom du fichier destinataire : ')
    fs = open(fSource, 'r')
    fd = open(fDest, 'w')
    while 1:
        ligne = fs.readline()           # lire une ligne du fichier source
        if ligne =="" or ligne =="\n":
            break
        liste = traduire(ligne)         # la convertir en une liste
        liste = encodage(liste)         # y ajouter les données supplémentaires
        enregistrer(liste)              # sauvegarder dans fichier dest.
    
    fd.close()
    fs.close()
    
  10. # Recherche de lignes particulières dans un fichier texte :
    
    def chercheCP(ch):
        "recherche dans ch la portion de chaîne contenant le code postal"
        i, f, ns = 0, 0, 0          # ns est un compteur de codes #
        cc = ""                     # chaîne à construire 
        while i < len(ch):
            if ch[i] =="#":
                ns = ns +1
                if ns ==3:          # le CP se trouve après le 3e code #
                    f = 1           # variable "drapeau" (flag)
                elif ns ==4:        # inutile de lire après le 4e code #
                    break
            elif f ==1:             # le caractère lu fait partie du
                cc = cc + ch[i]     # CP recherché -> on mémorise
            i = i +1
        return cc    
            
    nomF = raw_input("Nom du fichier à traiter : ")
    codeP = raw_input("Code postal à rechercher : ")
    fi = open(nomF, 'r')
    while 1:
        ligne = fi.readline()
        if ligne =="":
            break
        if chercheCP(ligne) == codeP:
            print ligne
    fi.close()
    
  11. Réfléchissez !
  12. Réfléchissez !

Exercices

  1. Complétez l'exercice (mini-système de base de données) en lui ajoutant deux fonctions : l'une pour enregistrer le dictionnaire résultant dans un fichier texte, et l'autre pour reconstituer ce dictionnaire à partir du fichier correspondant. Chaque ligne de votre fichier texte correspondra à un élément du dictionnaire. Elle sera formatée de manière à bien séparer :
    • la clé et la valeur (c'est-à-dire le nom de la personne, d'une part, et l'ensemble : « âge + taille », d'autre part.
    • dans l'ensemble « âge + taille », ces deux données numériques.
    Vous utiliserez donc deux caractères séparateurs différents, par exemple « @ » pour séparer la clé et la valeur, et « # » pour séparer les données constituant cette valeur :
    Juliette@18#1.67
    Jean-Pierre@17#1.78
    Delphine@19#1.71
    Anne-Marie@17#1.63
    
    etc.
    
  2. Améliorez encore le script de l'exercice précédent, en utilisant un dictionnaire pour diriger le flux d'exécution du programme au niveau du menu principal. Votre programme affichera par exemple :
    Choisissez :
    (R)écupérer un dictionnaire préexistant sauvegardé dans un fichier
    (A)jouter des données au dictionnaire courant
    (C)onsulter le dictionnaire courant
    (S)auvegarder le dictionnaire courant dans un fichier
    (T)erminer :
    
    Suivant le choix opéré par l'utilisateur, vous effectuerez alors l'appel de la fonction correspondante en la sélectionnant dans un dictionnaire de fonctions.

Solution

  1. Sauvegarde d'un dictionnaire :
    def enregistrement():
        fich = raw_input("Entrez le nom du fichier de sauvegarde : ")
        ofi = open(fich, "w")
        # parcours du dictionnaire entier, converti au préalable en une liste :
        for cle, valeur in dico.items(): 
            # utilisation du formatage des chaînes pour créer l'enregistrement :
            ofi.write("%s@%s#%s\n" % (cle, valeur[0], valeur[1]))
        ofi.close()
    
    def lectureFichier():
        fich = raw_input("Entrez le nom du fichier de sauvegarde : ")
        try:
            ofi = open(fich, "r")
        except:
            print "*** fichier inexistant ***"
            return
    
        while 1:
            ligne = ofi.readline()
            if ligne =='':              # détection de la fin de fichier
                break
            enreg = ligne.split("@")    # restitution d'une liste [clé,valeur]
            cle = enreg[0]
            valeur = enreg[1][:-1]      # élimination du caractère de fin de ligne
            data = valeur.split("#")    # restitution d'une liste [âge, taille]
            age, taille = int(data[0]), float(data[1])
            dico[cle] = (age, taille)   # reconstitution du dictionnaire
        ofi.close()
    

    Ces deux fonctions peuvent être appelées respectivement à la fin et au début du programme principal, comme dans l'exemple ci-dessous :

    dico ={}
    lectureFichier()        
    while 1:
        choix = raw_input("Choisissez : (R)emplir - (C)onsulter - (T)erminer : ")
        if choix.upper() == 'T':
            break
        elif choix.upper() == 'R':
            remplissage()
        elif choix.upper() == 'C':
            consultation()
    enregistrement()
    
  2. Cet exercice complète le précédent. On ajoute encore deux petites fonctions, et on réécrit le corps principal du programme pour diriger le flux d'exécution en se servant d'un dictionnaire :
    # Contrôle du flux d'exécution à l'aide d'un dictionnaire
    def sortie():
        print "*** Job terminé ***"
        return 1                        # afin de provoquer la sortie de la boucle 
        
    def autre():
        print "Veuillez frapper R, A, C, S ou T, svp."
        
    
    dico ={}
    fonc ={"R":lectureFichier, "A":remplissage, "C":consultation,
           "S":enregistrement, "T":sortie}
    while 1:
        choix = raw_input("Choisissez :\n" +\
        "(R)écupérer un dictionnaire préexistant sauvegardé dans un fichier\n" +\
        "(A)jouter des données au dictionnaire courant\n" +\
        "(C)onsulter le dictionnaire courant\n" +\
        "(S)auvegarder le dictionnaire courant dans un fichier\n" +\
        "(T)erminer : ")
        # l'instruction ci-dessous appelle une fonction différente pour
        # chaque choix, par l'intermédiaire du dictionnaire <fonc> :
        if fonc.get(choix, autre)():
            break
        # Rem : toutes les fonctions appelées ici renvoient <None> par défaut,
        #       sauf la fonction sortie() qui renvoie 1 => sortie de la boucle
    

Lister des fichiers et dossiers

modifier
    items = os.walk(folder)
    for root, directories, files in items:
        for directory in directories:
            print(directory)
        for file in files:
            print(file)
    return

Créer un dossier s'il n'existe pas

modifier
file_path = 'subfolder/test.txt'
parent_folder = os.path.dirname(file_path)

if not os.path.exists(parent_folder):
    os.mkdir(parent_folder)

file_object = codecs.open(fichier, 'a', 'utf-8')

Supprimer des fichiers et dossiers

modifier

Pour un fichier :

file_path = 'subfolder/test.txt'
os.unlink(file_path)

Pour un dossier vide :

os.rmdir(dir_path)

Pour un dossier non vide :

import shutil
shutil.rmtree(dir_path)

Références

modifier
  1. Dans le cas de Windows, vous pouvez également inclure dans ce chemin la lettre qui désigne le périphérique de stockage où se trouve le fichier. Par exemple : D:/home/jules/exercices.
  2. Le point séparateur exprime donc ici une relation d'appartenance. Il s'agit d'un exemple de la qualification des noms qui sera de plus en plus largement exploitée dans la suite de ce cours. Relier ainsi des noms à l'aide de points est une manière de désigner sans ambiguïté des éléments faisant partie d'ensembles, lesquels peuvent eux-mêmes faire partie d'ensembles plus vastes, etc. Par exemple, l'étiquette systeme.machin.truc désigne l'élément truc, qui fait partie de l'ensemble machin, lequel fait lui-même partie de l'ensemble systeme. Nous verrons de nombreux exemples de cette technique de désignation, notamment lors de notre étude des classes d'objets.
  3. Suivant le système d'exploitation utilisé, le codage correspondant au marqueur de fin de ligne peut être différent. Sous Windows, par exemple, il s'agit d'une séquence de deux caractères (Retour chariot et Saut de ligne), alors que dans les systèmes de type Unix (comme Linux) il s'agit d'un seul saut de ligne, MacOS pour sa part utilisant un seul retour chariot. En principe, vous n'avez pas à vous préoccuper de ces différences. Lors des opérations d'écriture, Python utilise la convention en vigueur sur votre système d'exploitation. Pour la lecture, Python interprète correctement chacune des trois conventions (qui sont donc considérées comme équivalentes).
  4. En anglais, le terme pickle signifie "conserver". Le module a été nommé ainsi parce qu'il sert effectivement à enregistrer des données en conservant leur type.

Sources

modifier