Programmation Python

Fichiers
& I/O

Lire, écrire et naviguer dans le système de fichiers — texte, CSV, JSON et binaire.

open() & modes d'ouverture

open(fichier, mode, encoding) est la fonction centrale. Elle retourne un objet fichier à utiliser dans un bloc with.

"r"
Lecture
Défaut. Erreur si inexistant.
"w"
Écriture
Crée ou écrase le fichier.
"a"
Ajout
Écrit à la fin sans écraser.
"x"
Création
Erreur si le fichier existe déjà.
"r+"
Lecture+écriture
Lecture et écriture, curseur au début.
"b"
Binaire
Combiné : "rb", "wb"…
open() — syntaxe complète
# Toujours spécifier l'encodage explicitement
f = open("data.txt", "r", encoding="utf-8")

# Autres encodages courants
open("old.txt", encoding="latin-1")   # fichiers Windows anciens
open("data.csv", encoding="utf-8-sig") # CSV Excel (BOM)

# newline="" obligatoire pour csv.writer sur Windows
open("out.csv", "w", newline="", encoding="utf-8")
⚠️

Ne jamais omettre encoding="utf-8" — l'encodage par défaut dépend du système (CP1252 sur Windows, UTF-8 sur Linux/Mac). Cela provoque des bugs silencieux difficiles à reproduire.

Lecture de fichiers

Méthodes de lecture
with open("data.txt", encoding="utf-8") as f:

    # Tout le contenu en une chaîne
    contenu = f.read()

    # Remettre le curseur au début
    f.seek(0)

    # Une ligne à la fois
    ligne = f.readline()         # inclut le \n

    # Toutes les lignes dans une liste
    lignes = f.readlines()       # [ligne1\n, ligne2\n, ...]

# ✓ Idiome préféré — itération ligne par ligne
# (ne charge pas tout en mémoire)
with open("grand_fichier.txt", encoding="utf-8") as f:
    for ligne in f:               # générateur — efficace
        ligne = ligne.rstrip("\n")
        print(ligne)
Lecture avec gestion d'erreurs
from pathlib import Path

def lire_fichier(chemin: str) -> str | None:
    try:
        with open(chemin, encoding="utf-8") as f:
            return f.read()
    except FileNotFoundError:
        print(f"Fichier introuvable : {chemin}")
    except PermissionError:
        print(f"Accès refusé : {chemin}")
    except UnicodeDecodeError:
        print(f"Encodage incorrect : {chemin}")
    return None

# Raccourci pathlib (Python 3.5+)
contenu = Path("data.txt").read_text(encoding="utf-8")
💡

Pour les fichiers volumineux, toujours itérer ligne par ligne plutôt que read() qui charge tout en mémoire. Un fichier de 2 Go avec read() = 2 Go de RAM.

Écriture de fichiers

Écriture — write, writelines
# Écriture simple (écrase le fichier)
with open("sortie.txt", "w", encoding="utf-8") as f:
    f.write("Ligne 1\n")
    f.write("Ligne 2\n")

# writelines : liste de chaînes (sans \n auto)
lignes = ["alpha\n", "beta\n", "gamma\n"]
with open("sortie.txt", "w", encoding="utf-8") as f:
    f.writelines(lignes)

# Ajout à la fin du fichier
with open("log.txt", "a", encoding="utf-8") as f:
    f.write(f"[2024-03-01] Événement enregistré\n")

# Raccourci pathlib
from pathlib import Path
Path("sortie.txt").write_text(
    "contenu", encoding="utf-8"
)
print() vers un fichier
# print() accepte un paramètre file=
with open("rapport.txt", "w", encoding="utf-8") as f:
    print("=== Rapport ===", file=f)
    print(f"Total : {42}", file=f)
    print("Fin", file=f, end="")  # sans \n final

# Utile : flush() pour écrire immédiatement
# (éviter perte de données si crash)
with open("live.log", "a", encoding="utf-8") as f:
    f.write("données critiques\n")
    f.flush()  # force l'écriture disque
ℹ️

Le mode "w" écrase le fichier sans avertissement. Pour écrire sans risque d'écraser, utiliser "x" qui lève FileExistsError si le fichier existe déjà.

Context manager — with

Le bloc with garantit la fermeture du fichier même en cas d'exception. C'est l'unique façon correcte d'ouvrir un fichier en Python.

Pourquoi with est indispensable
# ✗ MAUVAIS — f.close() jamais appelé si exception
f = open("data.txt")
contenu = f.read()    # ← si exception ici
f.close()             # ← jamais exécuté !

# ✓ BON — fermeture garantie dans tous les cas
with open("data.txt", encoding="utf-8") as f:
    contenu = f.read()
# f.close() appelé automatiquement ici

# Ouvrir plusieurs fichiers simultanément
with (
    open("entree.txt", encoding="utf-8") as src,
    open("sortie.txt", "w", encoding="utf-8") as dst,
):
    for ligne in src:
        dst.write(ligne.upper())
Créer son propre context manager
from contextlib import contextmanager

@contextmanager
def fichier_temporaire(chemin: str):
    """Crée un fichier, le supprime après usage."""
    import os
    f = open(chemin, "w", encoding="utf-8")
    try:
        yield f          # ← corps du with
    finally:
        f.close()
        os.remove(chemin) # toujours nettoyé

with fichier_temporaire("tmp.txt") as f:
    f.write("données temporaires")
# fichier supprimé ici automatiquement

CSV

csv.reader & csv.writer
import csv

# Lecture
with open("employes.csv", encoding="utf-8") as f:
    lecteur = csv.reader(f, delimiter=";")
    en_tete = next(lecteur)   # sauter la première ligne
    for ligne in lecteur:
        nom, age, salaire = ligne
        print(f"{nom} — {age} ans")

# Écriture
donnees = [
    ["nom", "age", "ville"],
    ["Alice", 30, "Bruxelles"],
    ["Bob",   25, "Liège"],
]
with open("sortie.csv", "w",
          newline="", encoding="utf-8") as f:
    writer = csv.writer(f, delimiter=";")
    writer.writerows(donnees)
DictReader & DictWriter (recommandé)
import csv

# DictReader : chaque ligne = dict
with open("employes.csv", encoding="utf-8") as f:
    for ligne in csv.DictReader(f, delimiter=";"):
        print(ligne["nom"], ligne["salaire"])

# DictWriter : écrire depuis des dicts
employes = [
    {"nom": "Alice", "age": 30, "ville": "BXL"},
    {"nom": "Bob",   "age": 25, "ville": "LGE"},
]
champs = ["nom", "age", "ville"]
with open("out.csv", "w", newline="",
          encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=champs, delimiter=";")
    w.writeheader()
    w.writerows(employes)
⚠️

Toujours ouvrir un CSV en écriture avec newline="" — sinon csv.writer ajoute des lignes vides supplémentaires sur Windows à cause du double \r\n.

JSON

json — lecture et écriture
import json

# Lire un fichier JSON → dict Python
with open("config.json", encoding="utf-8") as f:
    config = json.load(f)

# Écrire un dict Python → fichier JSON
data = {
    "nom": "Alice",
    "scores": [10, 20, 30],
    "actif": True,
}
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=2, ensure_ascii=False)

# Sans fichier : chaîne ↔ dict
texte = json.dumps(data, indent=2)   # dict → str
data2 = json.loads(texte)              # str → dict
Correspondances types Python ↔ JSON
# Python → JSON
dict   → object  { }
list   → array   [ ]
tuple  → array   [ ]
str    → string  ""
int    → number
float  → number
True   → true
False  → false
None   → null

# Attention : JSON ne supporte pas
# les objets Python arbitraires (datetime, set…)
import json
from datetime import datetime

def serialiser(obj):
    if isinstance(obj, datetime):
        return obj.isoformat()
    raise TypeError(f"Non sérialisable : {type(obj)}")

json.dumps({"date": datetime.now()}, default=serialiser)
💡

ensure_ascii=False permet d'écrire les caractères accentués tels quels (é, à, ü…) au lieu de les encoder en \u00e9. Toujours l'utiliser avec encoding="utf-8".

Fichiers binaires & pickle

Mode binaire — rb / wb
# Copier un fichier binaire (image, PDF…)
with open("photo.jpg", "rb") as src, \
     open("copie.jpg", "wb") as dst:
    while chunk := src.read(4096):  # walrus operator
        dst.write(chunk)

# Lire les N premiers octets (magic bytes)
with open("fichier", "rb") as f:
    magic = f.read(4)
    if magic == b"\x89PNG":
        print("C'est un PNG")
    elif magic[:2] == b"\xff\xd8":
        print("C'est un JPEG")
pickle — sérialisation d'objets Python
import pickle

# Sauvegarder n'importe quel objet Python
donnees = {"scores": [10, 20], "joueur": "Alice"}

with open("save.pkl", "wb") as f:
    pickle.dump(donnees, f)

# Recharger exactement le même objet
with open("save.pkl", "rb") as f:
    donnees = pickle.load(f)
⚠️

Ne jamais charger un fichier pickle provenant d'une source non fiable — il peut exécuter du code arbitraire lors du load(). Pour l'échange de données, préférer JSON.

pathlib — chemins orientés objet

pathlib.Path remplace avantageusement os.path. Les chemins sont des objets avec des méthodes — plus lisibles et portables (Windows/Linux/Mac).

pathlib — opérations courantes
from pathlib import Path

p = Path("dossier/sous-dossier/fichier.txt")

# Propriétés du chemin
p.name       # "fichier.txt"
p.stem       # "fichier"
p.suffix     # ".txt"
p.parent     # Path("dossier/sous-dossier")
p.parts      # ("dossier", "sous-dossier", "fichier.txt")

# Construction de chemins avec /
base = Path("projet")
config = base / "config" / "settings.json"

# Chemin absolu
p.resolve()              # /home/user/projet/...
Path.cwd()               # répertoire courant
Path.home()              # répertoire home

# Tests d'existence et de type
p.exists()               # True / False
p.is_file()              # True si c'est un fichier
p.is_dir()               # True si c'est un dossier
pathlib — créer, lister, supprimer
from pathlib import Path

dossier = Path("mon_projet/data")

# Créer un dossier (et parents si besoin)
dossier.mkdir(parents=True, exist_ok=True)

# Lister le contenu
for f in dossier.iterdir():
    print(f.name)

# Chercher des fichiers (glob)
for f in Path(".").glob("**/*.py"):  # récursif
    print(f)

# Lire / écrire directement
texte = Path("readme.txt").read_text(encoding="utf-8")
Path("readme.txt").write_text("contenu", encoding="utf-8")

# Renommer et supprimer
p.rename(p.parent / "nouveau_nom.txt")
p.unlink()                 # supprimer un fichier
dossier.rmdir()            # supprimer un dossier vide

# Métadonnées
stat = p.stat()
print(stat.st_size)        # taille en octets
print(stat.st_mtime)       # date de modification

os & shutil

os — interface système
import os

# Variables d'environnement
home = os.environ.get("HOME", "/tmp")
os.environ["MA_VAR"] = "valeur"

# Répertoire courant
cwd = os.getcwd()
os.chdir("/tmp")

# Exécuter une commande système
code = os.system("ls -la")    # retourne le code de retour

# Chemin système (équivalent pathlib)
chemin = os.path.join("dossier", "fichier.txt")
os.path.exists(chemin)
os.path.basename(chemin)    # "fichier.txt"
os.path.dirname(chemin)     # "dossier"
shutil — copier, déplacer, archiver
import shutil

# Copier un fichier
shutil.copy("src.txt", "dst.txt")         # sans métadonnées
shutil.copy2("src.txt", "dst.txt")        # avec métadonnées

# Copier un dossier entier
shutil.copytree("src/", "dst/")

# Déplacer / renommer
shutil.move("ancien.txt", "nouveau.txt")

# Supprimer un dossier et tout son contenu
shutil.rmtree("dossier/")  # DANGER : irréversible

# Créer une archive ZIP
shutil.make_archive("archive", "zip", "dossier/")

# Espace disque disponible
total, used, free = shutil.disk_usage("/")
print(f"Libre : {free // 2**30} Go")
💡

Préférer pathlib pour la navigation et shutil pour les opérations "lourdes" (copie d'arbres, archives). Les deux coexistent très bien.

Pièges classiques

PiègeSymptômeSolution
Encodage omisCaractères corrompus (é → é) selon le systèmeToujours encoding="utf-8"
close() oubliéDonnées perdues, fichier verrouilléToujours with open(...)
newline= oublié (CSV)Lignes vides sur Windowsopen(..., newline="")
Mode "w" par erreurFichier écrasé silencieusementVérifier Path.exists() avant, ou utiliser "x"
Chemin relatif fragileFileNotFoundError selon le répertoire d'exécutionUtiliser Path(__file__).parent / "fichier"
pickle non fiableErreur si classe modifiée entre sauvegarde et chargementVersionner les classes ou utiliser JSON
Chemin relatif au script (recommandé)
from pathlib import Path

# __file__ = chemin absolu du script courant
BASE = Path(__file__).parent        # dossier du script
CONFIG = BASE / "config.json"      # toujours trouvé
DATA   = BASE / "data" / "input.csv"

# Fonctionne peu importe d'où on lance le script

Cheat sheet

Fichiers texte

open(f, "r", encoding="utf-8")Lecture
open(f, "w", encoding="utf-8")Écriture (écrase)
open(f, "a", encoding="utf-8")Ajout
f.read()Tout le contenu
for ligne in f:Ligne par ligne
f.write("texte\n")Écrire

CSV & JSON

csv.DictReader(f)Lire CSV → dicts
csv.DictWriter(f, fields)Écrire CSV depuis dicts
json.load(f)Fichier JSON → dict
json.dump(d, f, indent=2)Dict → fichier JSON
json.loads(s)String → dict
json.dumps(d)Dict → string

pathlib.Path

Path("a") / "b" / "c"Construire un chemin
p.exists()Existe ?
p.mkdir(parents=True)Créer dossier
p.glob("**/*.py")Chercher des fichiers
p.read_text(encoding=)Lire directement
p.unlink()Supprimer

shutil

shutil.copy(src, dst)Copier fichier
shutil.copytree(s, d)Copier dossier
shutil.move(src, dst)Déplacer
shutil.rmtree(path)Supprimer dossier
shutil.make_archive()Créer archive ZIP