Aller au contenu

Fichiers et exceptions

Jusqu’ici, toutes nos données vivaient dans le code. Cependant, très souvent les données qu’on souhaite traiter avec un programme ne sont pas connues d’avance et ne seront pas entrées une à une par l’utilisateur.

Cette page t’apprend à aller chercher des données à l’extérieur de ton programme, dans des fichiers, et à réagir quand quelque chose tourne mal en cours de route.


Tu connais déjà deux façons de fournir des données à un programme :

SourceExempleLimite
Prédéfinie (dans le code)prix = 4.50
notes = [78, 85, 92]
Figé dans le code
Saisie utilisateur (input)input("Ton nom? ")Une valeur à la fois

Ces deux approches sont très limitantes et créent une dépendance avec les programmeurs (pour la mise à jour du code) ou avec l’utilisateur (pour fournir les données).

Les fichiers brisent cette limite. Un fichier CSV exporté d’un tableur (ex: Excel), un rapport téléchargé, un journal de transactions — toutes ces sources évoluent indépendamment de ton programme. Ton code va les lire, les transformer, et potentiellement écrire les résultats dans un nouveau fichier ou modifier le fichier source.


Pour accéder à un fichier, Python a besoin de deux informations : le chemin vers le fichier et le mode d’accès.

fichier = open("donnees.csv", "r")
ModeSignification
"r"Lecture (read) — le fichier doit exister
"w"Écriture (write) — crée le fichier ou écrase le contenu existant
"a"Ajout (append) — ajoute à la fin sans effacer

Une fois le fichier ouvert, on peut accéder (lire) au contenu :

Avec la méthode read, tout le contenu du fichier est lu et retourné dans une seule chaîne de caractères.

fichier = open("donnees.csv", "r")
contenu = fichier.read()
fichier.close()
print(contenu)
produit,quantite,prix_unitaire
Café,150,4.50
Thé,85,3.75
Chocolat chaud,60,5.25
Jus d'orange,120,4.00
Limonade,95,3.50

Avec la méthode readlines, tout le contenu est lu et retourné sous forme de liste où chaque élément est une ligne du fichier.

fichier = open("donnees.csv", "r")
lignes = fichier.readlines()
fichier.close()
print(lignes)
['produit,quantite,prix_unitaire\n', 'Café,150,4.50\n', 'Thé,85,3.75\n', ...]

Tu as remarqué les appels à fichier.close() dans les exemples précédents. Oublier de fermer un fichier peut causer des problèmes : données non sauvegardées, fichier verrouillé, mémoire gaspillée. Le problème, c’est qu’on oublie souvent de fermer.

Python offre une solution élégante : le bloc with.

with open("donnees.csv", "r") as fichier:
contenu = fichier.read()
print("Dans le with — fichier ouvert :", not fichier.closed)
print("Après le with — fichier fermé :", fichier.closed)
Dans le with — fichier ouvert : True
Après le with — fichier fermé : True

Le with ferme automatiquement le fichier dès qu’on sort du bloc, même si une erreur survient entre-temps. C’est la seule façon d’ouvrir un fichier dans ce cours. Oublie open()/close() séparés.

Les fichiers texte utilisent un encodage pour représenter les caractères. Sur macOS et Linux, c’est généralement UTF-8 par défaut. Sur Windows, c’est parfois autre chose — et là, les accents deviennent du charabia.

Pour éviter les surprises, précise toujours l’encodage :

with open("donnees.csv", "r", encoding="utf-8") as fichier:
contenu = fichier.read()

CSV signifie Comma-Separated Values — des valeurs séparées par des virgules (ou autres caractères). C’est le format universel pour les données tabulaires. Un fichier CSV ressemble à ceci :

produit,quantite,prix_unitaire
Café,150,4.50
Thé,85,3.75
Chocolat chaud,60,5.25

La première ligne contient les en-têtes (les noms de colonnes). Chaque ligne suivante est un enregistrement. Les valeurs sont séparées par des virgules.

Tu pourrais lire un CSV ligne par ligne et découper avec split(",") :

with open("donnees_ventes.csv", "r") as fichier:
lignes = fichier.readlines()
en_tete = lignes[0].strip().split(",")
print("En-tête :", en_tete)
for ligne in lignes[1:]:
valeurs = ligne.strip().split(",")
nom = valeurs[0]
quantite = int(valeurs[1])
prix = float(valeurs[2])
total = quantite * prix
print(nom + " : " + str(quantite) + " x " + str(prix) + " $ = " + str(total) + " $")
En-tête : ['produit', 'quantite', 'prix_unitaire']
Café : 150 x 4.5 $ = 675.0 $
Thé : 85 x 3.75 $ = 318.75 $
Chocolat chaud : 60 x 5.25 $ = 315.0 $
Jus d'orange : 120 x 4.0 $ = 480.0 $
Limonade : 95 x 3.5 $ = 332.5 $

Ça fonctionne tant que les données sont simples. Cependant, regarde ce qui arrive quand une valeur contient elle-même une virgule :

nom,adresse,ville
"Tremblay, Marc","123, rue Principale",Montréal
ligne = '"Tremblay, Marc","123, rue Principale",Montréal'
print(ligne.split(","))
['"Tremblay', ' Marc"', '"123', ' rue Principale"', 'Montréal']

Cinq morceaux au lieu de trois. Le split(",") coupe bêtement à chaque virgule, sans considérer les guillemets.

Le module csv de Python gère correctement les guillemets, les virgules à l’intérieur des champs, les sauts de ligne dans les valeurs.

import csv
with open("donnees_ventes.csv", "r") as fichier:
lecteur = csv.reader(fichier)
en_tete = next(lecteur)
print("En-tête :", en_tete)
for rangee in lecteur:
print(rangee)
En-tête : ['produit', 'quantite', 'prix_unitaire']
['Café', '150', '4.50']
['Thé', '85', '3.75']
['Chocolat chaud', '60', '5.25']
["Jus d'orange", '120', '4.00']
['Limonade', '95', '3.50']

Chaque rangée est une liste de chaînes. Pour accéder à la quantité, tu utilises rangee[1]. La fonction next() avance le lecteur d’une ligne.

import csv
with open("donnees_ventes.csv", "r") as fichier:
lecteur = csv.DictReader(fichier)
for rangee in lecteur:
nom = rangee["produit"]
quantite = int(rangee["quantite"])
prix = float(rangee["prix_unitaire"])
print(nom + "" + str(quantite) + " unités à " + str(prix) + " $")
Café — 150 unités à 4.5 $
Thé — 85 unités à 3.75 $
Chocolat chaud — 60 unités à 5.25 $
Jus d'orange — 120 unités à 4.0 $
Limonade — 95 unités à 3.5 $

DictReader utilise la première ligne comme clés. Chaque rangée devient un dictionnaire, rendant même l’adressage dans le code plus lisible: rangee["produit"] dit clairement ce qu’on va chercher.


Très similaire à la lecture, on ouvre (accède) d’abord le fichier en mode écriture (w), puis on utilise la méthode write pour ajouter du contenu au fichier.

with open("resultats.txt", "w") as fichier:
fichier.write("Rapport de ventes\n")
fichier.write("=================\n")
fichier.write("Total : 2121.25 $\n")
import csv
ventes = [
["Café", 150, 4.50],
["Thé", 85, 3.75],
["Chocolat chaud", 60, 5.25],
]
with open("export.csv", "w", newline="") as fichier:
ecrivain = csv.writer(fichier)
ecrivain.writerow(["produit", "quantite", "prix_unitaire"])
for vente in ventes:
ecrivain.writerow(vente)

The first computer bug

On ne peut pas toujours prévoir une erreur, mais on peut prévoir un chemin alternatif en cas d’erreur.

Tu as sûrement déjà vu ces messages en rouge dans ta console. Parfois, l’erreur ne vient pas de ton code, mais plutôt des données.

Traceback (most recent call last):
File "script.py", line 3, in <module>
ValueError: invalid literal for int() with base 10: 'abc'
ErreurCauseExemple
ValueErrorConversion impossibleint("abc")
IndexErrorIndex hors limitesnotes[5] quand la liste a 3 éléments
KeyErrorClé absente du dictionnaireetudiant["age"] quand la clé n’existe pas
TypeErrorOpération entre types incompatibles"5" + 3
ZeroDivisionErrorDivision par zéro10 / 0

La lecture de fichiers introduit une nouvelle catégorie d’erreurs — celles qui dépendent de l’environnement et non de la logique du programme :

ErreurCause
FileNotFoundErrorLe fichier n’existe pas au chemin donné
PermissionErrorPas les droits de lecture ou d’écriture
UnicodeDecodeErrorMauvais encodage

Ces erreurs sont particulières : tu ne peux pas les prévenir en regardant ton code. Le fichier peut exister sur ton ordinateur mais pas sur celui de ton collègue.
Les données peuvent être propres dans un fichier et corrompues dans un autre. Ton programme doit pouvoir réagir plutôt que planter.


La plupart des langages de programmation de haut niveau offrent un mécanisme facilitant le traitement des exceptions. Bien entendu, cela inclut également Python.

Si une opération ou un appel de fonction risque de causer une erreur ou de soulever une exception, il faut encapsuler l’instruction dans un contexte permettant de traiter l’erreur.

try:
fichier = open("inexistant.csv", "r")
except FileNotFoundError:
print("Le fichier n'existe pas!")
Le fichier n'existe pas!

Le bloc try contient le code risqué. Si une erreur du type spécifié survient, Python saute au bloc except au lieu de planter. Le reste du programme continue normalement.

try:
numerateur = 10
denominateur = 0
result = numerateur / denominateur
print(result)
except ZeroDivisionError:
print("Erreur (Division par zéro): Le dénominateur ne peut être égal à 0.")
Erreur (Division par zéro): Le dénominateur ne peut être égal à 0.

Le mot-clé as capture l’objet d’erreur dans une variable :

try:
fichier = open("inexistant.csv", "r")
except FileNotFoundError as e:
print("Erreur : " + str(e))
Erreur : [Errno 2] No such file or directory: 'inexistant.csv'

Un même bloc try peut avoir plusieurs blocs except, chacun pour un type d’erreur différent :

def lire_csv(chemin):
resultats = []
try:
with open(chemin, "r") as fichier:
lignes = fichier.readlines()
en_tete = lignes[0].strip().split(",")
for i in range(1, len(lignes)):
valeurs = lignes[i].strip().split(",")
rangee = {}
for j in range(len(en_tete)):
rangee[en_tete[j]] = valeurs[j]
resultats.append(rangee)
except FileNotFoundError:
print("Erreur : le fichier '" + chemin + "' n'existe pas.")
except IndexError:
print("Erreur : le fichier semble mal formaté.")
return resultats
try:
tab_pair = [12, 4, 8, 20]
print(tab_pair[5])
except ZeroDivisionError:
print("Dénominateur ne peut être égal à 0")
except IndexError:
print("Index en dehors des limites du tableau.")
Index en dehors des limites du tableau.

Python essaie les blocs except dans l’ordre. Dès qu’il trouve le bon type, il exécute ce bloc et ignore les suivants.

flowchart TD
    A["Début du bloc <b>try</b>"] --> B["Exécution du code risqué"]
    B --> C{"Une erreur<br>survient?"}
    
    C -- "Non" --> D["Le bloc <b>try</b> se termine<br>normalement"]
    D --> G["Suite du programme ✅"]
    
    C -- "Oui" --> E{"Le type correspond<br>à un <b>except</b>?"}
    
    E -- "Oui" --> F["Le bloc <b>except</b><br>correspondant s'exécute"]
    F --> G
    
    E -- "Non" --> H["❌ Le programme plante"]