Tom Daluzeau
← Parcours Python · fichiers
Python · fichiers · leçon 1 / 4Ch. 2 · Fichiers12 minTerminée

Fichiers : with open, modes, pathlib

Ouvrir un fichier avec with open, choisir le bon mode (r, w, a), le lire ligne par ligne en utf-8, construire un chemin avec pathlib et attraper un fichier absent.

Mis à jour le

À recouper avec le cours. Cette leçon a été écrite à partir du plan du chapitre, avant relecture avec les supports. En cas de doute, le cours fait foi.

Pourquoi on voit ça

Jusqu’ici, la liste des serveurs était écrite en dur dans le code. Un vrai outil d’administration lit son inventaire dans un fichier et écrit ses résultats dans un autre. Changer le parc ne doit pas obliger à modifier le programme.

En DevOps, tout passe par des fichiers : inventaires, configurations, journaux, rapports. Avant de lire du JSON ou du CSV, il faut savoir ouvrir un fichier proprement, le retrouver sur le disque et réagir quand il manque.

Dans toute la leçon, servers.txt contient ceci :

web-1
db-1
cache-1

Ouvrir un fichier : with open

with open("servers.txt", encoding="utf-8") as f:
    content = f.read()
print(content)
web-1
db-1
cache-1
  • open(...) ouvre le fichier et as f lui donne un nom.
  • f.read() renvoie tout le contenu dans une seule chaîne.
  • with ferme le fichier tout seul à la sortie du bloc, même si une erreur arrive dedans. C’est pour ça qu’on écrit toujours with open.

Une fois sorti du bloc, le fichier est fermé : f.closed vaut True et un f.read() lèverait une erreur. La variable content, elle, reste utilisable.

Les modes : r, w, a

Le deuxième argument de open est le mode.

Mode Sert à Si le fichier existe S’il n’existe pas
"r" (par défaut) lire on le lit FileNotFoundError
"w" écrire il est vidé il est créé
"a" ajouter à la fin on écrit à la suite il est créé
with open("checks.log", "w", encoding="utf-8") as f:
    f.write("web-1 ok\n")
with open("checks.log", "a", encoding="utf-8") as f:
    f.write("db-1 ok\n")
with open("checks.log", encoding="utf-8") as f:
    print(f.read())
web-1 ok
db-1 ok
checks.log avant web-1 ok mode "w" vidé dès l'ouverture mode "a" on écrit à la suite db-1 ok la ligne web-1 ok est perdue web-1 ok db-1 ok
Même write("db-1 ok\n"), deux résultats : "w" écrase, "a" ajoute à la fin.

Deux pièges :

  • "w" efface tout, dès l’ouverture. Avec "w" aux deux ouvertures, il ne resterait que db-1 ok.
  • write n’ajoute pas de retour à la ligne. C’est à toi d’écrire \n à la fin de chaque ligne.

Lire ligne par ligne

Un fichier se parcourt avec for : un tour de boucle par ligne.

with open("servers.txt", encoding="utf-8") as f:
    for line in f:
        print(line.strip())
web-1
db-1
cache-1

Chaque ligne lue se termine par \n. Sans strip(), print ajoute son propre retour à la ligne et tu obtiens une ligne vide entre chaque nom. Pire : line == "web-1" serait faux, car line vaut "web-1\n". Prends l’habitude d’écrire line.strip().

le disque servers.txt des octets open encoding="utf-8" octets → texte for line in f "web-1\n" une ligne par tour line.strip() "web-1" prêt à comparer sans strip, line == "web-1" est faux : il reste le \n au bout de la ligne
Du fichier sur le disque à une chaîne propre : décoder, découper en lignes, nettoyer.

L’encodage : toujours utf-8

Un fichier contient des octets. L’encodage est la règle qui les transforme en caractères. Si tu ne le précises pas, Python prend celui du système, qui n’est pas le même partout. Le même script peut donc marcher sur ton poste et abîmer les accents sur un serveur.

with open("owner.txt", "w", encoding="utf-8") as f:
    f.write("Zoé\n")
with open("owner.txt", encoding="latin-1") as f:
    print(f.read())
Zoé

Écrit en utf-8, relu avec un autre encodage : le é est cassé. La règle : encoding="utf-8" à chaque open, en lecture comme en écriture.

pathlib : des chemins propres

Le module pathlib fournit le type Path, un chemin de fichier avec ses outils.

from pathlib import Path

p = Path("data") / "servers.txt"
print(p)
print(p.exists())
print(p.read_text(encoding="utf-8"))
data/servers.txt
True
web-1
db-1
cache-1
Écriture Ce que ça fait
Path("data") fabrique un chemin
Path("data") / "servers.txt" assemble deux morceaux (ce / n’est pas une division)
p.exists() True si le fichier ou le dossier existe, False sinon, sans jamais planter
p.read_text(encoding="utf-8") ouvre, lit tout, ferme, et renvoie une chaîne

L’opérateur / met le bon séparateur selon le système. C’est plus sûr que de coller des chaînes avec +.

Chemin relatif : relatif à quoi ?

"servers.txt" est un chemin relatif. Il part du dossier courant, celui où se trouve ton terminal quand tu lances la commande. Pas du dossier du script.

Imagine fleetcheck.py et servers.txt rangés ensemble dans le dossier projet :

cd projet
python3 fleetcheck.py          # fonctionne
cd ..
python3 projet/fleetcheck.py   # FileNotFoundError

Le script n’a pas changé. C’est le dossier courant qui a changé : Python cherche maintenant servers.txt un étage trop haut. Quand un fichier « a disparu », demande-toi d’abord d’où tu as lancé la commande.

$ cd /home/tom/projet $ python3 fleetcheck.py /home/tom projet/ dossier courant fleetcheck.py servers.txt cherche projet/servers.txt il est là : le fichier est lu $ cd /home/tom $ python3 projet/fleetcheck.py /home/tom dossier courant projet/ fleetcheck.py servers.txt cherche /home/tom/servers.txt absent : FileNotFoundError
open("servers.txt") part du dossier courant du terminal, pas du dossier où est rangé le script.

Quand le fichier pose problème : OSError

Ouvrir en lecture un fichier absent lève FileNotFoundError :

FileNotFoundError: [Errno 2] No such file or directory: 'absent.txt'

FileNotFoundError fait partie d’une famille plus large, OSError : toutes les erreurs qui viennent du système (fichier absent, droits insuffisants, dossier à la place d’un fichier…).

try:
    with open("absent.txt", encoding="utf-8") as f:
        print(f.read())
except FileNotFoundError as e:
    print("Fichier introuvable :", e)
Fichier introuvable : [Errno 2] No such file or directory: 'absent.txt'
  • except FileNotFoundError : tu ne traites que le fichier absent.
  • except OSError : tu traites tous les problèmes de fichier d’un coup, fichier absent compris.

Si tu écris les deux, mets le plus précis en premier, comme pour toute hiérarchie d’exceptions.

À retenir

  • with open(chemin, mode, encoding="utf-8") as f: ferme le fichier tout seul à la sortie du bloc.
  • "r" lit (erreur si absent), "w" écrase, "a" ajoute à la fin. write n’ajoute pas de \n.
  • for line in f : une ligne par tour, avec son \n au bout. Pense à strip().
  • Path("data") / "servers.txt" assemble un chemin ; exists() teste, read_text() lit tout.
  • Un chemin relatif part du dossier courant, pas du dossier du script.
  • Fichier absent : FileNotFoundError, un enfant de OSError.

Entraînement

12 questions, dont 7 à compléter. Tu réponds, on passe à la suivante. La note arrive à la fin.

Cartes · 8

Réviser les cartes ici

Espace : retourner