Aller au contenu

Chp F. Lecture/écriture dans un fichier

On s’intéresse dans ce chapitre aux Entrées/Sorties fichier, autrement dit à la lecture ou à l'écriture de données dans un fichier. En effet, lorsque l'on souhaite sauvegarder des informations et les récupérer entre diverses sessions, il est indispensable de pouvoir les transférer depuis la mémoire vive (RAM) vers un support physique (type disque dur). Il y a de nombreuses façon de procéder, et on se contentera ici d'utiliser une méthode universelle par le biais de lecture/écriture de fichiers en mode texte.

Remarques :

  • on se servira beaucoup des listes dans ce TD. Si vous n'ĂŞtes pas parfaitement au clair, vous pouvez revoir le TDB2, ou si vous voulez tout savoir (ou presque) sur ce type de donnĂ©es, aller par exemple lire les pages correspondantes sur coding game ou encore sur W3school. Cela dit, rien ne presse pour l'instant, on aura un chapitre entier consacrĂ© aux listes un peu plus tard dans l'annĂ©e...

  • on utilisera aussi les dictionnaires. Il n'y a rien sur coding game, mais on n'utilisera que des choses simples, et on fera quelques rappels si besoin. Prenez un peu de temps pour consulter la section dĂ©diĂ©e aux dictionnaires sur le site W3school.

1. Principe python pour accéder à un fichier

Pour accéder à un fichier avec python, on procède en deux temps:

  1. On commence par créer un objet python de type file avec la fonction open(nom, mode) où nom est une chaîne de caractères précisant le nom du fichier et mode est aussi une chaîne de caractères précisant le mode d'accès (typiquement 'r' pour read, 'w' pour write, mais il y en a d'autres...)

  2. Celle-ci renvoie donc un objet qui fera en quelque sorte le pont entre la mémoire vive et la mémoire physique. On va pouvoir agir sur cet objet (écrire ou lire des données).

Afin d'éviter des pertes de données ou autres déconvenues, dans les version récentes de python on utilise la fonction open en conjonction avec l'instruction with. Celle-ci s'occupe entre autres choses de fermer le fichier pour nous.

Accéder à un fichier

with open(nomfich, mode = "w") as myfile :
    # bloc de code pour écrire ou lire...

Remarque : mettre "r" à la place de "w" pour accéder en lecture...

On verra des exemples complets dans les paragraphes suivants...

L'objet de type file créé par la fonction open est un itérable (on en a déjà parlé dans le chapitre sur les boucles...).

Pour infos voici quelques unes des méthodes supportées pour un objet file en python :

métode action
.read() retourne la totalité du fichier comme un str
.readline() retourne la ligne courante du fichier comme un str
.readlines() retourne la liste de toute les lignes du fichier
 .write(txt) écrit txt (type str) dans le fichier
.writelines(lines) écrit lines (liste de str) dans le fichier (un élément par ligne)

2. Un petit complément sur les chaînes de caractères

On a déjà vu en début d'année le type str (abréviation de string) pour les chaînes de caractères. Parmi tous les caractères classiques que l'on a déjà utilisés (lettres alphabétiques, chiffres, ponctuation etc.) il faut en ajouter deux autres pour bien comprendre ce chapitre :

  • le caractère \n (c'est un caractère spĂ©cial, codĂ© avec la barre oblique inverse, antislash) : il marque un retour Ă  la ligne. En effet dans un texte, quand on veut aller Ă  la ligne, il faut bien le stipuler, c'est avec ce caractère que l'on procède. Par exemple :

    print("Bonjour\ntout le monde")
    

    va écrire :

    Bonjour
    tout le monde
    
  • le caractère \t marque une tabulation. C'est un caractère spĂ©cial, qui insère un nombre d'espace variable de façon Ă  se caler sur une colonne particulière. Par exemple :

    print("a\tbcdefg")
    print("ab\tcdefg")
    print("abc\tdefg")
    

    va écrire :

    a   bcdefg
    ab  cdefg
    abc defg
    

    Ce sera pratique pour formater un peu nos fichiers texte.

3. Quelques rudiments sur les dictionnaires

Un dictionnaire est une structure de données qui permet de stocker des paires clé/valeur. Chaque valeur est associée à une clé unique, ce qui permet un accès rapide aux données. En fait, vu de loin, c'est un peu comme une liste, mais au lieu d'accéder aux éléments par leur position (indice), on y accède par une clé qui peut être de n'importe quel type immuable (comme des chaînes de caractères, des nombres, etc.). D'ailleurs nous allons voir que la syntaxe est très proche de celle des listes.

Voici comment créer et utiliser un dictionnaire en Python :

Création d'un dictionnaire en python

# Création d'un dictionnaire
mon_dictionnaire = {
    'clé1': 'valeur1',
    'clé2': 'valeur2',
    'clé3': 'valeur3'
}

print(mon_dictionnaire)

Accès à une valeur via sa clé

# Accès à une valeur via sa clé
valeur = mon_dictionnaire['clé2']  # valeur sera 'valeur2'
print(valeur)

Ajout ou modification d'une paire clé/valeur

# Ajout ou modification d'une paire clé/valeur
mon_dictionnaire['clé4'] = 'valeur4'  # Ajoute une nouvelle paire
mon_dictionnaire['clé2'] = 'nouvelle_valeur2'  # Modifie la valeur associée à 'clé2'
print(mon_dictionnaire)

Suppression d'une paire clé/valeur

# Suppression d'une paire clé/valeur
del mon_dictionnaire['clé3']  # Supprime la paire avec 'clé3'
print(mon_dictionnaire)

Itération sur un dictionnaire

# Itération sur un dictionnaire
# méthode 1 : itérer sur les clés
for cle in mon_dictionnaire.keys():
    print(f"Clé: {cle}, Valeur: {mon_dictionnaire[cle]}")

# méthode 2 : itérer sur les valeurs
for valeur in mon_dictionnaire.values():
    print(f"Valeur: {valeur}")

# méthode 3 : itérer sur les paires clé/valeur
for cle, valeur in mon_dictionnaire.items():
    print(f"Clé: {cle}, Valeur: {valeur}")

Tester la création et l'utilisation d'un dictionnaire dans la fenêtre ci-dessous :

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Entrer ou sortir du mode "deux colonnes"
(Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran"
(Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier

4. Écriture dans un fichier texte

Un premier exemple rudimentaire :

Écriture d'une chaîne dans un fichier texte avec la méthode .write(...)

Voici un programme élémentaire et détaillé permettant d'écrire des lignes de texte dans un fichier.

1
2
3
4
5
6
with open('fichier_test.txt', mode='w') as myfile : # on note mode='w' pour écriture
    myfile.write('La première ligne du fichier\n') # on écrit une première ligne 
    myfile.write('Une autre ligne\n') 
    myfile.write('Un troisième sans le caractère de fin de ligne') #(1)
    myfile.write("Voyez comment ce texte n'est pas sur la 4e ligne !\n")
print(f"le fichier 'fichier_test.txt' a été crée") # on informe l'utilisateur de la création du fichier
  1. On n'a pas mis le \n, du coup pas de retour Ă  la ligne

L'affichage du script donne :

le fichier 'fichier_test.txt' a été crée

Si l'on ouvre le fichier texte fichier_test.txt avec un éditeur de texte basique (style notepad), on aura :

La première ligne du fichier
Une autre ligne
Un troisième sans le caractère de fin de ligneVoyez comment ce texte n'est pas sur la 4e ligne !

Voyons ensuite sur un exemple détaillé avec des listes:

Écriture dans un fichier texte

Voici un programme complet et détaillé permettant d'écrire dans un fichier texte le tableau de valeur de la fonction carrée, ainsi que le contenu du fichier crée (remarquez bien le découpage en fonctions élémentaires):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# on déclare deux fonctions :

def f(x : float) -> float : # (1)!
return x*x

def save(nomfich : str, datas : list):
    """
    Reçoit un nom de fichier et une liste de données
    (chacun des éléments de cette liste étant une chaîne
    de caractère terminée par '\n'), et sauvegarde ces
    données dans un fichier sur le disque dur
    """
    with open(nomfich, mode='w') as myfile : # on note mode='w' pour écriture
        myfile.write('# x\tf(x)\n') # on écrit une première ligne avec les labels
        for line in datas :
            myfile.write(line) #(2)!
    print(f"le fichier {nomfich} a été crée") # on informe l'utilisateur de la création du fichier

# Début du programme principal

# 1. création de la liste des couples du tableau de valeur de la fonction :
l_couples = []
for x in range(11):
    # option 1 : concaténation avec la fonction 'str(...)' qui convertit l'argument en chaîne de caractères
    chaine1 = str(x) + '\t' + str(f(x)) + '\n'

    # option 2 : directement avec une f-string
    chaine2 = f"{x}\t{f(x)}\n"

    l_couples.append(chaine2)

print(l_couples)   

# 2. sauvegarde des données dans un fichier :
save(nomfich='carree.txt', datas=l_couples)
  1. La fonction "carrée"
  2. On peut noter que l'on aurait pu raccourcir le code en remplaçant la boucle directement par myfile.writelines(datas)

L'affiche du script donne :

['0\t0\n', '1\t1\n', '2\t4\n', '3\t9\n', '4\t16\n', '5\t25\n', '6\t36\n', '7\t49\n', '8\t64\n', '9\t81\n', '10\t100\n']
le fichier carree.txt a été crée

Et le fichier texte carree.txt contiendra :

# x    f(x)
0      0
1      1
2      4
3      9
...
10     100

5. Lecture de données dans un fichier

Pour ouvrir un fichier en lecture le principe est le même que pour l'écriture mais on modifie la variable mode pour passer en lecture : mode='r' (r pour read).

On peut lire un fichier en entier et stocker son contenu dans une seule variable, ou bien lire le fichier ligne par ligne...

5.a Lecture d'un fichier en entier

Voyons cela sur un exemple détaillé. Récupérons le contenu du fichier carree.txt crée dans la section précédente :

Lecture du fichier carree.txt

def readmyfile(nomfich : str) -> str :
    """
    récupère et renvoie la chaine de caractères contenue dans le fichier
    nommé 'nomfich'
    """
    with open(nomfich, mode='r') as myfile :
        whole_text = myfile.read()
    return whole_text

# on teste :
my_text = readmyfile('carree.txt')
print(my_text)
# x    f(x)
0      0
1      1
2      4
3      9
4      16
5      25
6      36
7      49
8      64
9      81
10     100

Le contenu est bien affiché, mais ce format n'est pas très pratique si l'on veut récupérer les données numériques du fichier pour par exemple faire une représentation graphique. Voyons comment faire cela dans le paragraphe suivant.

5.b Lecture d'un fichier ligne par ligne

Reprenons le fichier carree.txt et récupérons les données lignes par ligne afin d'extraire les données numériques séparément au lieu de récupérer uniquement des chaînes de caractères.

Lecture ligne par ligne

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def read_file(filename):
    """ reçoit un nom de fichier 'nomfich' et renvoie le contenu
    de ce fichier sous forme d'une liste de chaîne de caractères
    (chaque élément de la liste contient une ligne du fichier texte)
    Remarque : 
        Les lignes commençant par # ainsi que les lignes vides sont exclues"""
    datas = []
    with open(filename, mode='r') as myfile : # (1)!
        for line in myfile :
            if line[0] not in ['#','\n'] : # (2)!
                datas.append(line)
return datas

datas = read_file('carree.txt')         
print(f"datas = {datas}")
  1. On ouvre le fichier en lecture
  2. On exclut les lignes vides ou commençant par #
datas = ['0\t0\n', '1\t1\n', '2\t4\n', '3\t9\n', '4\t16\n', '5\t25\n', '6\t36\n', '7\t49\n', '8\t64\n', '9\t81\n', '10\t100\n']

On constate que l'on a récupéré cette fois-ci une liste de chaine de caractères. Il ne reste qu'à extraire les données numériques.

Quelques remarques :

  • Contrairement au paragraphe prĂ©cĂ©dent, l'affichage des donnĂ©es est moins convivial, mais on va voir que sous cette forme, les donnĂ©es seront plus facilement exploitables...
  • Noter au passage le rĂ´le de la ligne 10 (if line[0] not in ['#','\n'] :) qui permet de ne pas lire les lignes commençant par le symbole '#' ou les lignes vides (celles commençant par '\n').
  • La variable datas est donc une liste de str. Il reste alors Ă  traiter chacune de ces chaĂ®nes pour rĂ©cupĂ©rer les valeurs numĂ©riques sĂ©parĂ©ment. Pour cela, on a besoin de sĂ©parer les termes de chaque Ă©lĂ©ment de la liste. Il existe une instruction bien pratique dans python, la mĂ©thode  .split() qui s'applique sur des chaines de caractères. Si on ne prĂ©cise rien en paramètres de la mĂ©thode, tout espace blanc est sĂ©parateur (espaces, tabulations (\t), retour Ă  la ligne (\n),...).Toutefois elle peut prendre deux paramètres optionnels qui ne nous serviront pas ici (voir doc officielle par exemple ou W3school).

Voyons donc comment récupérer simplement les valeurs numériques contenues dans la variable datas:

Extraction des données numériques

# récupération des données numériques contenues dans la liste 'datas':
def analyse_datas(datas : list) -> tuple :
    """
    reçoit une liste de chaînes de caractères (chacune
    contenant deux nombres séparés par un espace blanc)
    et renvoie deux listes de float : la première avec
    les premières valeurs, la seconde avec les secondes valeurs
    """
    x , y = [] , [] # les deux listes sont vides
    for p in datas :
        ( x1 , x2 )= p.split() # on scinde la chaîne en deux
        x.append(float(x1)) # on n'oublie pas de convertir les str en float !
        y.append(float(x2))
    return (x,y)

x,y = analyse_datas(datas)
print("x=",x)
print("y=",y)

Bien noter :

  • la conversion des chaines stren nombre de type float pour pouvoir faire des calculs pas la suite...

  • l'utilisation de la mĂ©thode .split() qui coupe une chaine et renvoie les diffĂ©rentes parties dans une liste.

x= [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
y= [0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0, 81.0, 100.0]

On peut alors très facilement, avec le code ci-dessous, faire par exemple une représentation graphique de la fonction carrée. On verra cela en détails dans un prochain chapitre consacré aux bibliothèques...

import matplotlib.pyplot as plt

plt.clf()
plt.plot(x,y)
plt.show()

courbe fonction carrée

6. Les TD F

Le TD F1 : Premières activités sur les fichiers

Dans ce TD, on va utiliser les notions vues dans ce chapitre pour faire quelques exercices simples de lecture/écriture de fichiers. On parlera de tables de multiplications et encore de palindromes...

TD F1 version interactive capytale

Versions html : Énoncé et Correction

Le TD F2 : Décrypter un texte chiffré par une méthode d'analyse fréquentielle des lettres

TD F2 version interactive capytale

Versions html : Énoncé et Correction

Le TD F3 : Récupérer des données numériques dans un fichier texte et faire quelques analyses (sortie d'un cycliste)

TD F3 version interactive capytale

Versions html : Énoncé et Correction

Le TD F4 : Pour ceux qui sont en avance : analyser les coupes du monde de football

TD F4 version interactive capytale

Versions html : Énoncé et Correction (à venir)