Tutoriels gratuits et ouverts à toutes et à tous · conçus au Lycée Chevrollier — AngersÀ propos

Série « Parcours Python » · partie 7 / 8

Python niveau 6 — Fichiers et erreurs

Conserver des données entre deux exécutions et écrire des programmes qui ne plantent pas : niveau 6 du parcours Python, avec exercices corrigés et QCM.

Développement✎ B. Jousseaume📅 17 octobre 2026⏱ 5 h📶 Avancé

1. Les exceptions

Quand Python rencontre un problème à l’exécution, il lève une exception et le programme s’arrête. Tu en as déjà croisé plusieurs :

ExceptionCause typiqueExemple
ValueErrorValeur de mauvais formatint("vingt")
ZeroDivisionErrorDivision par zéro10 / 0
IndexErrorIndice hors de la liste[1, 2][5]
KeyErrorClé absente du dictionnaire{"a": 1}["b"]
FileNotFoundErrorFichier introuvableopen("absent.txt")
TypeErrorOpération entre types incompatibles"a" + 1

2. try / except

On place le code « risqué » dans un bloc try. Si une exception se produit, Python saute dans le bloc except correspondant au lieu de planter.

try:
    age = int(input("Âge : "))
    print(f"Dans 10 ans tu auras {age + 10} ans.")
except ValueError:
    print("Ce n'est pas un nombre entier.")

On peut prévoir plusieurs cas et ajouter else (exécuté si tout s’est bien passé) et finally (exécuté dans tous les cas) :

try:
    a = int(input("a : "))
    b = int(input("b : "))
    resultat = a / b
except ValueError:
    print("Saisie invalide.")
except ZeroDivisionError:
    print("Division par zéro impossible.")
else:
    print(f"Résultat : {resultat}")
finally:
    print("Fin du calcul.")

La saisie robuste, enfin

On peut maintenant terminer la fonction du niveau 4 :

def demander_entier(question, mini, maxi):
    while True:
        try:
            n = int(input(question))
        except ValueError:
            print("Merci de taper un nombre entier.")
            continue
        if mini <= n <= maxi:
            return n
        print(f"Le nombre doit être entre {mini} et {maxi}.")

3. Lire et écrire des fichiers texte

La fonction open(chemin, mode) ouvre un fichier. On l’utilise toujours avec with, qui referme automatiquement le fichier, même en cas d’erreur.

ModeSignification
"r"Lecture (par défaut). Erreur si le fichier n’existe pas.
"w"Écriture. Crée le fichier ou écrase son contenu !
"a"Ajout à la fin (append). Crée le fichier s’il n’existe pas.
# Écrire
with open("journal.txt", "w", encoding="utf-8") as f:
    f.write("Première ligne\n")          # \n = retour à la ligne
    f.write("Deuxième ligne\n")

# Ajouter
with open("journal.txt", "a", encoding="utf-8") as f:
    f.write("Ligne ajoutée\n")

# Lire ligne par ligne
with open("journal.txt", "r", encoding="utf-8") as f:
    for ligne in f:
        print(ligne.strip())               # strip() retire le \n final

4. Le format CSV

Le CSV (valeurs séparées par des virgules ou des points-virgules) est le format des tableurs. Le module csv le gère proprement.

import csv

eleves = [
    {"nom": "Alice", "note": 14},
    {"nom": "Bruno", "note": 9},
]

# Écriture
with open("notes.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["nom", "note"], delimiter=";")
    writer.writeheader()
    writer.writerows(eleves)

# Lecture
with open("notes.csv", newline="", encoding="utf-8") as f:
    for ligne in csv.DictReader(f, delimiter=";"):
        print(ligne["nom"], int(ligne["note"]))   # tout est lu comme du texte

Le séparateur ; permet d’ouvrir le fichier directement dans un tableur configuré en français.

5. Le format JSON

Le JSON sauvegarde facilement des listes et des dictionnaires, même imbriqués. C’est le format le plus utilisé pour échanger des données sur le web.

import json

contacts = {"Léo": {"tel": "0600000000", "email": "leo@exemple.fr"}}

with open("contacts.json", "w", encoding="utf-8") as f:
    json.dump(contacts, f, indent=2, ensure_ascii=False)

with open("contacts.json", encoding="utf-8") as f:
    contacts = json.load(f)

Gérer le fichier absent

def charger(chemin):
    try:
        with open(chemin, encoding="utf-8") as f:
            return json.load(f)
    except FileNotFoundError:
        return {}                     # premier lancement : rien à charger
    except json.JSONDecodeError:
        print("Fichier corrompu, démarrage à vide.")
        return {}

6. Mini-projet : les contacts persistants

7. Bilan

Exercices corrigés

La robustesse se teste en essayant de casser son programme : taper du texte, rien du tout, des nombres négatifs, supprimer le fichier de données, le corrompre… Un programme bien écrit survit à tout cela.

Exercice 6.1 — Blinder ses programmes

Voir le corrigé
import random

secret = random.randint(1, 100)
essais = 0
while True:
    try:
        prop = int(input("Proposition : "))
    except ValueError:
        print("Un nombre entier, s'il te plaît.")
        continue
    essais += 1
    if prop < secret:
        print("Plus grand")
    elif prop > secret:
        print("Plus petit")
    else:
        print(f"Gagné en {essais} essais !")
        break

Explication

continue renvoie au début de la boucle sans compter l’essai : une faute de frappe ne pénalise pas le joueur. Seul int() est dans le try, pour ne pas masquer d’autres erreurs.

Pour aller plus loin

La calculatrice blindée (seule la fonction de saisie change) :

def demander_nombre(question):
    while True:
        try:
            return float(input(question).replace(",", "."))
        except ValueError:
            print("Nombre invalide, recommence.")

Le replace(",", ".") accepte aussi la virgule française : une petite attention qui améliore beaucoup l’expérience utilisateur.

Exercice 6.2 — Journal de bord

Voir le corrigé
from datetime import datetime

FICHIER = "journal.txt"

try:
    with open(FICHIER, encoding="utf-8") as f:
        lignes = f.readlines()
    for ligne in lignes[-5:]:
        print(ligne.strip())
except FileNotFoundError:
    print("Journal vide.")

phrase = input("Nouvelle entrée : ")
horodatage = datetime.now().strftime("%d/%m/%Y %H:%M")
with open(FICHIER, "a", encoding="utf-8") as f:
    f.write(f"[{horodatage}] {phrase}\n")

Explication

On lit d’abord (s’il existe) puis on ajoute avec le mode "a". readlines() renvoie une liste de lignes ; la tranche [-5:] garde les 5 dernières, même s’il y en a moins.

Mini-projet

Voir le corrigé
import json, csv
from datetime import datetime

FICHIER = "contacts.json"

def charger():
    try:
        with open(FICHIER, encoding="utf-8") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return {}

def sauvegarder(contacts):
    with open(FICHIER, "w", encoding="utf-8") as f:
        json.dump(contacts, f, indent=2, ensure_ascii=False)

def journaliser(action):
    date = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
    with open("historique.log", "a", encoding="utf-8") as f:
        f.write(f"{date} {action}\n")

def exporter_csv(contacts):
    with open("contacts.csv", "w", newline="", encoding="utf-8") as f:
        w = csv.writer(f, delimiter=";")
        w.writerow(["nom", "tel", "email"])
        for nom, c in sorted(contacts.items()):
            w.writerow([nom, c["tel"], c["email"]])
    print("Export terminé : contacts.csv")

# Dans main() : contacts = charger() au début,
# puis sauvegarder(contacts) et journaliser("ajout Léo")
# après chaque ajout ou suppression.

Explication

Le principe est de sauvegarder après chaque modification : en cas de coupure, rien n’est perdu. Voici le programme complet assemblé.

Pour aller plus loin

def main():
    contacts = charger()
    while True:
        choix = input("\n1 Ajouter  2 Afficher  3 Rechercher  4 Supprimer  5 Export CSV  6 Quitter > ")
        if choix == "1":
            nb_avant = len(contacts)
            ajouter(contacts)                     # fonction du niveau 5
            if len(contacts) > nb_avant:
                sauvegarder(contacts)
                journaliser("ajout")
        elif choix == "2":
            afficher(contacts)
        elif choix == "3":
            rechercher(contacts)
        elif choix == "4":
            nb_avant = len(contacts)
            supprimer(contacts)
            if len(contacts) < nb_avant:
                sauvegarder(contacts)
                journaliser("suppression")
        elif choix == "5":
            exporter_csv(contacts)
        elif choix == "6":
            break
        else:
            print("Choix inconnu.")

main()

Le journal note seulement le type d’action et pas le contenu : c’est un bon principe de minimisation des données (RGPD).

🧠 Testez vos connaissances

5 questions. Répondez puis cliquez sur « Corriger ». Rien n’est enregistré : c’est de l’auto-évaluation.

1. Que se passe-t-il avec int("vingt") ?

💬 C’est exactement ce que try/except permet d’intercepter.

2. Pourquoi éviter un except: sans nom d’exception ?

💬 On attrape toujours des exceptions précises, sinon les bugs deviennent invisibles.

3. Quel mode d’ouverture efface le contenu d’un fichier existant ?

💬 "w" écrase sans prévenir, "a" ajoute à la fin, "r" lit seulement.

4. À quoi sert with open(...) as f: ?

💬 Sans with, un fichier oublié ouvert peut perdre des données.

5. Pourquoi ne jamais utiliser eval() sur le contenu d’un fichier ?

💬 Un fichier piégé pourrait effacer des documents. On utilise json.load, int() ou float().

© 2026 Bruno Jousseaume. Ce cours est publié sous licence CC BY-NC-SA 4.0 : vous pouvez le partager et l’adapter en citant l’auteur et un lien vers cette page, sans usage commercial, et en partageant vos adaptations sous la même licence.

Pour aller plus loin