Opérations sur les fichiers en Python
Cet article explique les opérations sur les fichiers en Python.
En gardant à l'esprit la « sécurité », « l'efficacité » et la « lisibilité », ce guide explique les concepts étape par étape, des bases aux techniques pratiques.
YouTube Video
Opérations sur les fichiers en Python
La manipulation des fichiers est une compétence fondamentale essentielle, des petits scripts aux applications à grande échelle.
Ouverture et fermeture de fichiers
Voyons d'abord des exemples de lecture et d'écriture de fichiers texte. L'utilisation de l'instruction with (gestionnaire de contexte) garantit la fermeture correcte du fichier.
Le code suivant ouvre un fichier texte, lit son contenu et le traite ligne par ligne.
1# Open and read a text file safely using a context manager.
2with open("example.txt", "r", encoding="utf-8") as f:
3 # Read all lines lazily (as an iterator) and process each line.
4 for line in f:
5 print(line.rstrip())- Spécifier explicitement
encoding="utf-8"permet de réduire les problèmes liés aux différences de plateforme. - Même avec de gros fichiers,
for line in fest économe en mémoire.
Écriture (écrasement et ajout)
Faites attention au mode lorsque vous écrivez dans des fichiers. w permet d'écraser, a permet d'ajouter à la fin du fichier. Utilisez également l'instruction with lors de l'écriture.
Le code suivant montre des exemples de base d'écrasement et d'ajout.
1# Write (overwrite) to a file.
2with open("output.txt", "w", encoding="utf-8") as f:
3 f.write("First line\n")
4 f.write("Second line\n")
5
6# Append to the same file.
7with open("output.txt", "a", encoding="utf-8") as f:
8 f.write("Appended line\n")- Ce code écrit du texte dans
output.txt, puis ajoute du texte supplémentaire dans le même fichier. Le mode"w"écrase le fichier, tandis que le mode"a"ajoute une nouvelle ligne à la fin du contenu existant. - Si vous devez forcer l'écriture, appelez
flush(), mais en général, le contenu est vidé automatiquement à la fin du contexte. - Si plusieurs processus ou threads peuvent écrire en même temps, vous devez envisager un contrôle exclusif, comme le verrouillage de fichier.
Lecture et écriture de données binaires
Les images et fichiers compressés sont manipulés en mode binaire (rb ou wb). Contrairement au mode texte, encoding est ignoré.
Le code suivant lit un fichier binaire et le copie dans un autre fichier en mode binaire.
1# Read and write binary files.
2with open("image.png", "rb") as src:
3 data = src.read()
4
5with open("copy.png", "wb") as dst:
6 dst.write(data)- Pour les fichiers binaires volumineux, évitez de tout lire d'un coup avec
read(). Lire et écrire par portions consomme moins de mémoire.
Exemple de manipulation de gros fichiers par portions
Pour les fichiers très volumineux qui ne tiennent pas en mémoire, lisez et écrivez-les par morceaux de taille fixe. Puisque l'opération dépend de l'I/O, ajuster la taille du tampon est utile.
Le code suivant copie un fichier par blocs de 64 Ko. Cela permet une opération rapide tout en gardant une faible consommation de mémoire.
1# Copy a large file in chunks to avoid using too much memory.
2CHUNK_SIZE = 64 * 1024 # 64 KB
3
4with open("large_source.bin", "rb") as src, open("large_dest.bin", "wb") as dst:
5 while True:
6 chunk = src.read(CHUNK_SIZE)
7 if not chunk:
8 break
9 dst.write(chunk)- Vous pouvez ajuster la taille des blocs selon les caractéristiques de votre réseau ou disque. Sur les SSD, une taille de bloc légèrement supérieure fonctionne souvent mieux.
Exemple utilisant l'argument buffering
En spécifiant l'argument buffering dans la fonction open(), vous pouvez contrôler la taille du tampon utilisée en interne par Python. Cela vous permet d'optimiser davantage l'efficacité des entrées/sorties.
1# Explicitly set the internal buffer size to 128 KB for faster I/O.
2CHUNK_SIZE = 64 * 1024
3BUFFER_SIZE = 128 * 1024 # 128 KB internal buffer
4
5with open("large_source.bin", "rb", buffering=BUFFER_SIZE) as src, open("large_dest.bin", "wb", buffering=BUFFER_SIZE) as dst:
6 while True:
7 chunk = src.read(CHUNK_SIZE)
8 if not chunk:
9 break
10 dst.write(chunk)- Si vous définissez la valeur de
bufferingà 0, les E/S sont effectuées sans mise en tampon ; 1 active la mise en tampon par ligne, tandis que les valeurs de 2 ou plus utilisent un tampon de la taille spécifiée en octets. - En général, la valeur par défaut est suffisante car le système d'exploitation gère efficacement la mise en cache, mais ajuster ce paramètre peut être efficace pour des fichiers très volumineux ou des périphériques particuliers.
Opérations modernes sur les fichiers avec Pathlib
Le module standard pathlib rend la gestion des chemins plus intuitive. Cela améliore la lisibilité et la sécurité par rapport aux chemins sous forme de chaînes.
1from pathlib import Path
2
3path = Path("data") / "notes.txt"
4
5# Ensure parent directory exists.
6path.parent.mkdir(parents=True, exist_ok=True)
7
8# Write and read using pathlib.
9path.write_text("Example content\n", encoding="utf-8")
10content = path.read_text(encoding="utf-8")
11print(content)- Ce code montre comment créer un dossier avec
pathlib, écrire dans un fichier texte et en lire le contenu. Avec un objetPath, vous pouvez gérer les chemins de manière intuitive et sécurisée. Pathdispose d'API pratiques, telles queiterdir()etglob(). Vous pouvez écrire du code sans vous soucier des séparateurs de chemin selon le système d'exploitation.
Fichiers et dossiers temporaires (tempfile)
On peut créer des fichiers temporaires en toute sécurité avec tempfile. Cela permet d'éviter les conditions de course de sécurité et les collisions de noms.
Le code suivant montre un exemple de création de données temporaires avec un fichier temporaire.
1import tempfile
2
3# Create a temporary file that is deleted on close.
4with tempfile.NamedTemporaryFile(
5 mode="w+",
6 encoding="utf-8",
7 delete=True
8) as tmp:
9 tmp.write("temporary data\n")
10 tmp.seek(0)
11 print(tmp.read())
12
13# tmp is deleted here- Ce code crée un fichier temporaire, y écrit puis lit des données, et le supprime automatiquement à la fin du bloc
with. En utilisanttempfile.NamedTemporaryFile, vous pouvez gérer les fichiers temporaires en toute sécurité et sans conflit. Commedelete=Trueest spécifié, le fichier est supprimé automatiquement. - Sous Windows, il se peut que vous ne puissiez pas rouvrir le fichier immédiatement depuis un autre handle. Dans ce cas, réglez
delete=Falseet gérez la suppression manuellement.
shutil : opérations de haut niveau pour copier, déplacer et supprimer
La copie, le déplacement et la suppression récursive de fichiers et de répertoires sont facilités avec shutil.
1import shutil
2
3# Copy a file preserving metadata.
4shutil.copy2("source.txt", "dest.txt")
5
6# Move a file or directory.
7shutil.move("old_location", "new_location")
8
9# Remove an entire directory tree (use with caution).
10shutil.rmtree("some_directory")shutil.copy2copie également les métadonnées (comme la date de modification).moveessaiera de déplacer le fichier même si le renommage n'est pas possible.rmtreeest une opération dangereuse : vérifiez toujours et faites une sauvegarde avant de supprimer.
Métadonnées de fichiers (os.stat) et gestion des permissions
La taille, la date de modification et les permissions d'un fichier peuvent être lues et modifiées avec os et stat.
Le code suivant récupère les informations de base via os.stat et modifie les permissions avec os.chmod.
1import os
2import stat
3from datetime import datetime
4
5st = os.stat("example.txt")
6print("size:", st.st_size)
7print("modified:", datetime.fromtimestamp(st.st_mtime))
8
9# Make file read-only for owner.
10os.chmod("example.txt", stat.S_IREAD)- Les permissions fonctionnent différemment sous les systèmes POSIX et Windows. Si la compatibilité multi-plateforme est importante, utilisez des API de haut niveau ou gérez les cas particuliers.
Verrouillage de fichiers (contrôle exclusif) — différences entre Unix et Windows
Un contrôle exclusif est nécessaire si plusieurs processus accèdent au même fichier en parallèle. UNIX utilise fcntl, tandis que Windows utilise msvcrt.
Le code suivant utilise fcntl.flock sur les systèmes UNIX pour obtenir un verrou exclusif lors de l'écriture.
1# Unix-style file locking example
2import fcntl
3
4with open("output.txt", "a+", encoding="utf-8") as f:
5 fcntl.flock(f, fcntl.LOCK_EX)
6 try:
7 f.write("Safe write\n")
8 f.flush()
9 finally:
10 fcntl.flock(f, fcntl.LOCK_UN)- Ce code obtient un verrou exclusif avec
fcntl.flocksur les systèmes de type UNIX, afin d'écrire en sécurité et d'éviter les écritures simultanées. Libérez toujours le verrou après le traitement pour permettre l'accès aux autres processus. - Sous Windows, utilisez
msvcrt.locking(). Pour une utilisation haut niveau, envisagez des bibliothèques externes commeportalockeroufilelock.
Modèles d'écriture atomique de fichiers
Pour éviter la corruption de fichier lors des mises à jour, écrivez dans un fichier temporaire puis remplacez-le avec os.replace en cas de succès.
Écrire dans un fichier temporaire puis le remplacer permet d'éviter la corruption en cas de plantage.
1import os
2from pathlib import Path
3import tempfile
4
5def atomic_write(path: Path, data: str, encoding="utf-8"):
6 # Write to a temp file in the same directory and atomically replace.
7 dirpath = path.parent
8 with tempfile.NamedTemporaryFile(
9 mode="w",
10 encoding=encoding,
11 dir=str(dirpath),
12 delete=False
13 ) as tmp:
14 tmp.write(data)
15 tmp.flush()
16 tempname = tmp.name
17
18 # Atomic replacement (on most OSes)
19 os.replace(tempname, str(path))
20
21# Usage
22atomic_write(Path("config.json"), '{"key":"value"}\n')os.replace effectue un remplacement atomique à l'intérieur du même système de fichiers. Notez que l'atomicité n'est pas garantie entre différents montages.
Accès rapide avec mmap (pour les données volumineuses)
Pour un accès aléatoire à de gros fichiers, mmap améliore les performances d'entrée/sortie. Cela concerne principalement des opérations binaires.
Le code suivant effectue un mapping mémoire d'un fichier et lit ou écrit des plages d'octets spécifiques. Soyez prudent lors du changement de la taille du fichier.
1import mmap
2
3with open("data.bin", "r+b") as f:
4 mm = mmap.mmap(f.fileno(), 0) # map entire file
5 try:
6 print(mm[:20]) # first 20 bytes
7 mm[0:4] = b"\x00\x01\x02\x03" # modify bytes
8 finally:
9 mm.close()- Ce code mappe un fichier binaire en mémoire avec
mmapet effectue des lectures/écritures directes au niveau des octets. Le mappage mémoire permet un accès rapide et aléatoire aux grands ensembles de données. mmapest efficace, mais une mauvaise utilisation peut entraîner des problèmes de cohérence des données. Appelezflush()pour synchroniser si besoin.
CSV / JSON / Pickle : lecture et écriture par format
Certains formats de données disposent de modules dédiés. Utilisez csv pour CSV, json pour JSON et pickle pour sauvegarder des objets Python.
Le code suivant propose des exemples de base pour la lecture et l'écriture de CSV et JSON, ainsi que l'utilisation de Pickle. Comme Pickle peut exécuter du code arbitraire, évitez de charger des données provenant de sources non fiables.
1import csv
2import json
3import pickle
4
5# CSV write
6with open("rows.csv", "w", encoding="utf-8", newline="") as f:
7 writer = csv.writer(f)
8 writer.writerow(["name", "age"])
9 writer.writerow(["Alice", 30])
10
11# JSON write
12data = {"items": [1, 2, 3]}
13with open("data.json", "w", encoding="utf-8") as f:
14 json.dump(data, f, ensure_ascii=False, indent=2)
15
16# Pickle write (only for trusted environments)
17obj = {"key": "value"}
18with open("obj.pkl", "wb") as f:
19 pickle.dump(obj, f)- Il est recommandé de spécifier
newline=""pour CSV afin d'éviter les lignes vides supplémentaires sous Windows. Avecensure_ascii=False, les caractères UTF-8 restent lisibles dans le JSON.
Lecture et écriture directe de fichiers compressés (gzip / bz2 / zipfile)
Gérer directement gzip et zip permet d'économiser de l'espace disque. La bibliothèque standard inclut les modules correspondants.
Le code ci-dessous est un exemple simple de lecture et d'écriture de fichiers gzip en mode texte.
1import gzip
2
3# Write gzipped text.
4with gzip.open("text.gz", "wt", encoding="utf-8") as f:
5 f.write("Compressed content\n")
6
7# Read gzipped text.
8with gzip.open("text.gz", "rt", encoding="utf-8") as f:
9 print(f.read())- Il existe un compromis entre le taux de compression et la vitesse en fonction du niveau et du format de compression.
Mesures de sécurité et de vulnérabilité
Les points suivants peuvent être pris en compte pour les mesures de sécurité et de vulnérabilité.
- N’utilisez jamais de données non fiables directement dans les noms de fichiers ou chemins.
- Utilisez Pickle uniquement avec des sources fiables.
- Minimisez les privilèges d’exécution et n’accordez que les permissions minimales aux processus manipulant les fichiers.
- Utilisez des fichiers temporaires avec
tempfileet ne stockez pas de fichiers non chiffrés dans des répertoires publics.
Si vous utilisez une entrée utilisateur dans les chemins de fichiers, la normalisation et la validation sont nécessaires. Par exemple, utilisez Path.resolve() et vérifiez les répertoires parents.
1from pathlib import Path
2
3def safe_path(base_dir: Path, user_path: str) -> Path:
4 candidate = (base_dir / user_path).resolve()
5 if base_dir.resolve() not in candidate.parents and base_dir.resolve() != candidate:
6 raise ValueError("Invalid path")
7 return candidate
8
9# Usage
10base = Path("/srv/app/data")
11
12# Raises an exception: attempted path traversal outside `base`
13safe = safe_path(base, '../etc/passwd')- Soyez particulièrement vigilant lors de l’utilisation d’entrées externes comme chemins de fichiers dans les applications web ou API publiques.
Résumé des modèles courants
- Utilisez toujours l’instruction
with(fermeture automatique). - Spécifiez explicitement
encodingpour les données textuelles. - Lisez et écrivez les gros fichiers par portions.
- Implémentez le verrouillage des fichiers pour les ressources partagées.
- Pour les mises à jour critiques, utilisez le modèle atomique « écriture dans un fichier temporaire → os.replace ».
- Confirmez toujours et créez des sauvegardes avant d’effectuer des opérations dangereuses (comme la suppression ou l’écrasement).
- Normalisez et validez lors de l’utilisation d’entrées externes comme chemins de fichiers.
Résumé
Pour les opérations sur les fichiers, il est important d’utiliser des techniques sûres et fiables comme l’utilisation des blocs with, la spécification explicite de l’encodage et l’écriture atomique. Pour le traitement à grande échelle ou l’accès en parallèle, il est nécessaire de mettre en place des systèmes de verrouillage et de gestion des journaux afin d’éviter la corruption et les conflits de données. L'équilibre entre efficacité et sécurité est la clé pour des opérations de fichiers fiables.
Vous pouvez suivre l'article ci-dessus avec Visual Studio Code sur notre chaîne YouTube. Veuillez également consulter la chaîne YouTube.