Skip to content

Module 05 - Fichiers & I/O

Manipuler des fichiers et répertoires avec Python.

Durée estimée : 15 minutes


Objectifs du Module

  • Lire et écrire des fichiers texte et binaires
  • Utiliser pathlib pour la manipulation de chemins
  • Maîtriser les context managers
  • Gérer les permissions et métadonnées

1. Lecture de Fichiers

Méthode Traditionnelle

# Lecture complète
file = open("/var/log/messages", "r")
content = file.read()
file.close()

# Avec context manager (recommandé)
with open("/var/log/messages", "r") as file:
    content = file.read()
# Fichier automatiquement fermé

# Lecture ligne par ligne
with open("/var/log/messages", "r") as file:
    for line in file:
        print(line.strip())

# Lecture en liste de lignes
with open("/var/log/messages", "r") as file:
    lines = file.readlines()

# Lecture avec limite
with open("/var/log/messages", "r") as file:
    first_100_chars = file.read(100)
    next_line = file.readline()

Modes d'Ouverture

Mode Description
r Lecture (défaut)
w Écriture (écrase)
a Ajout (append)
x Création exclusive
b Mode binaire
t Mode texte (défaut)
+ Lecture et écriture
# Exemples
open("file.txt", "r")    # Lecture texte
open("file.txt", "w")    # Écriture texte (écrase)
open("file.txt", "a")    # Ajout texte
open("file.bin", "rb")   # Lecture binaire
open("file.bin", "wb")   # Écriture binaire
open("file.txt", "r+")   # Lecture et écriture

Encodage

# Spécifier l'encodage
with open("file.txt", "r", encoding="utf-8") as f:
    content = f.read()

# Gérer les erreurs d'encodage
with open("file.txt", "r", encoding="utf-8", errors="ignore") as f:
    content = f.read()

# errors="replace" remplace les caractères invalides par ?
# errors="strict" lève une exception (défaut)

2. Écriture de Fichiers

Écriture Texte

# Écriture simple
with open("output.txt", "w") as f:
    f.write("Hello World\n")

# Écriture de plusieurs lignes
lines = ["line 1", "line 2", "line 3"]
with open("output.txt", "w") as f:
    for line in lines:
        f.write(line + "\n")

# writelines (pas de newline automatique)
with open("output.txt", "w") as f:
    f.writelines([line + "\n" for line in lines])

# print() vers fichier
with open("output.txt", "w") as f:
    print("Hello", "World", sep=", ", file=f)

Ajout (Append)

# Ajouter à un fichier existant
with open("log.txt", "a") as f:
    f.write(f"[{datetime.now()}] New entry\n")

Fichiers Binaires

# Lecture binaire
with open("image.png", "rb") as f:
    data = f.read()

# Écriture binaire
with open("copy.png", "wb") as f:
    f.write(data)

3. pathlib - Chemins Modernes

Création de Chemins

from pathlib import Path

# Chemins
home = Path.home()              # /home/user
cwd = Path.cwd()                # Répertoire courant
config = Path("/etc/nginx")
relative = Path("logs/app.log")

# Construction de chemins
log_file = config / "nginx.conf"  # /etc/nginx/nginx.conf
backup = Path("/backup") / "2024" / "01"

# Depuis une string
path = Path("/var/log/messages")

Propriétés de Chemin

path = Path("/var/log/nginx/access.log")

path.name           # 'access.log'
path.stem           # 'access'
path.suffix         # '.log'
path.suffixes       # ['.log']
path.parent         # Path('/var/log/nginx')
path.parents        # Tous les parents
path.parts          # ('/', 'var', 'log', 'nginx', 'access.log')
path.anchor         # '/'

# Chemin absolu
Path("logs").resolve()  # /home/user/project/logs
path.is_absolute()      # True

# Modification
path.with_name("error.log")    # /var/log/nginx/error.log
path.with_suffix(".bak")       # /var/log/nginx/access.bak

Test d'Existence et Type

path = Path("/var/log")

path.exists()       # True
path.is_file()      # False
path.is_dir()       # True
path.is_symlink()   # False
path.is_absolute()  # True

Lecture/Écriture avec pathlib

config_path = Path("/etc/myapp/config.txt")

# Lecture
content = config_path.read_text()
content = config_path.read_text(encoding="utf-8")
data = config_path.read_bytes()

# Écriture
config_path.write_text("key=value\n")
config_path.write_bytes(b"binary data")
logs = Path("/var/log")

# Liste des fichiers/dossiers
list(logs.iterdir())

# Glob patterns
list(logs.glob("*.log"))           # Fichiers .log directs
list(logs.glob("**/*.log"))        # Récursif
list(logs.rglob("*.log"))          # Équivalent récursif

# Filtrer
log_files = [f for f in logs.iterdir() if f.is_file() and f.suffix == ".log"]

Opérations sur Fichiers

from pathlib import Path

src = Path("source.txt")
dst = Path("dest.txt")

# Création
Path("logs").mkdir(exist_ok=True)
Path("a/b/c").mkdir(parents=True, exist_ok=True)

# Suppression
Path("temp.txt").unlink(missing_ok=True)  # Supprime fichier
Path("empty_dir").rmdir()                  # Supprime dossier vide

# Renommage
src.rename(dst)

# Copie (nécessite shutil)
import shutil
shutil.copy(src, dst)
shutil.copytree(Path("src_dir"), Path("dst_dir"))

4. Métadonnées et Permissions

Informations sur les Fichiers

from pathlib import Path
import os
import stat

path = Path("/etc/passwd")

# Statistiques
stats = path.stat()
stats.st_size           # Taille en bytes
stats.st_mtime          # Modification time (timestamp)
stats.st_atime          # Access time
stats.st_ctime          # Change time (metadata)
stats.st_mode           # Permissions
stats.st_uid            # User ID
stats.st_gid            # Group ID

# Conversion du timestamp
from datetime import datetime
mtime = datetime.fromtimestamp(stats.st_mtime)
print(f"Modified: {mtime}")

Permissions

import os
import stat
from pathlib import Path

path = Path("script.sh")

# Lire les permissions
mode = path.stat().st_mode
is_readable = bool(mode & stat.S_IRUSR)
is_writable = bool(mode & stat.S_IWUSR)
is_executable = bool(mode & stat.S_IXUSR)

# Modifier les permissions
path.chmod(0o755)  # rwxr-xr-x
path.chmod(0o644)  # rw-r--r--

# Avec os
os.chmod("script.sh", stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP)

Propriétaire

import os
import pwd
import grp
from pathlib import Path

path = Path("/etc/passwd")
stats = path.stat()

# Obtenir le nom à partir de l'UID/GID
owner = pwd.getpwuid(stats.st_uid).pw_name
group = grp.getgrgid(stats.st_gid).gr_name

# Modifier le propriétaire (nécessite root)
os.chown("/path/to/file", uid, gid)

5. Fichiers Temporaires

import tempfile
from pathlib import Path

# Fichier temporaire auto-supprimé
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=True) as f:
    f.write("temporary content")
    f.flush()
    # Utiliser f.name pour le chemin
    print(f"Temp file: {f.name}")
# Fichier supprimé à la sortie du with

# Répertoire temporaire
with tempfile.TemporaryDirectory() as tmpdir:
    tmp_path = Path(tmpdir)
    (tmp_path / "file.txt").write_text("content")
    # Tout est supprimé à la sortie

# Créer sans auto-suppression
fd, path = tempfile.mkstemp(suffix=".log")
os.close(fd)
# N'oubliez pas de supprimer manuellement

# Obtenir le répertoire temp
tempfile.gettempdir()  # /tmp ou équivalent

6. Cas d'Usage SysOps

Lecture de Logs

from pathlib import Path
from collections import Counter

def analyze_nginx_log(log_path):
    """Analyse un log nginx."""
    status_codes = Counter()
    ips = Counter()

    with open(log_path) as f:
        for line in f:
            parts = line.split()
            if len(parts) >= 9:
                ip = parts[0]
                status = parts[8]
                ips[ip] += 1
                status_codes[status] += 1

    return {
        "top_ips": ips.most_common(10),
        "status_codes": dict(status_codes)
    }

Backup de Configuration

from pathlib import Path
from datetime import datetime
import shutil

def backup_config(config_path, backup_dir="/backup/configs"):
    """Crée une sauvegarde horodatée d'un fichier de config."""
    config_path = Path(config_path)
    backup_dir = Path(backup_dir)

    if not config_path.exists():
        raise FileNotFoundError(f"Config not found: {config_path}")

    backup_dir.mkdir(parents=True, exist_ok=True)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_name = f"{config_path.stem}_{timestamp}{config_path.suffix}"
    backup_path = backup_dir / backup_name

    shutil.copy2(config_path, backup_path)
    return backup_path

# Usage
backup_path = backup_config("/etc/nginx/nginx.conf")
print(f"Backup created: {backup_path}")

Rotation de Logs

from pathlib import Path

def rotate_logs(log_dir, pattern="*.log", keep=5):
    """Garde les N derniers fichiers de log."""
    log_dir = Path(log_dir)
    logs = sorted(log_dir.glob(pattern), key=lambda p: p.stat().st_mtime, reverse=True)

    for old_log in logs[keep:]:
        old_log.unlink()
        print(f"Deleted: {old_log}")

Exercices Pratiques

Exercice 1 : Lecteur de Config

# Créer une fonction read_config(path) qui :
# - Lit un fichier clé=valeur
# - Ignore les lignes vides et commentaires (#)
# - Retourne un dictionnaire

# Fichier config.ini :
# host=localhost
# port=8080
# # Ceci est un commentaire
# debug=true

Exercice 2 : Recherche de Fichiers

# Créer une fonction find_large_files(directory, min_size_mb) qui :
# - Parcourt récursivement un répertoire
# - Retourne les fichiers plus grands que min_size_mb
# - Inclut le chemin, la taille et la date de modification

Exercice 3 : Générateur de Rapport

# Créer une fonction disk_report(directory) qui :
# - Liste tous les fichiers récursivement
# - Calcule la taille totale par extension
# - Génère un rapport dans un fichier

Points Clés à Retenir

Bonnes Pratiques

  • Toujours utiliser with pour les fichiers
  • Préférer pathlib à os.path
  • Spécifier l'encodage explicitement
  • Gérer les fichiers absents avec exist_ok

Pièges Courants

  • Oublier de fermer les fichiers
  • Chemins relatifs vs absolus
  • Encodage incorrect (UTF-8 vs Latin-1)
  • Permissions insuffisantes

Voir Aussi


← Module 04 - Fonctions Module 06 - Formats de Données →

Retour au Programme