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 etas flui donne un nom.f.read()renvoie tout le contenu dans une seule chaîne.withferme le fichier tout seul à la sortie du bloc, même si une erreur arrive dedans. C’est pour ça qu’on écrit toujourswith 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
Deux pièges :
"w"efface tout, dès l’ouverture. Avec"w"aux deux ouvertures, il ne resterait quedb-1 ok.writen’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().
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.
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.writen’ajoute pas de\n.for line in f: une ligne par tour, avec son\nau 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 deOSError.
Entraînement
12 questions, dont 7 à compléter. Tu réponds, on passe à la suivante. La note arrive à la fin.
Ta note
Cartes · 8
Réviser les cartes ici
Pourquoi