Lire et écrire un fichier entier
os.WriteFile et os.ReadFile couvrent la plupart des besoins. Elles ouvrent, écrivent ou lisent, et ferment en un seul appel.
Les exemples de cette page travaillent dans un répertoire temporaire créé par os.MkdirTemp et le suppriment avec defer os.RemoveAll(dir), donc ils ne laissent rien derrière eux. Dans votre propre code, un chemin relatif comme "config.json" est résolu par rapport au répertoire de travail du processus, qui n'est pas forcément celui du fichier source ou du binaire.
os.WriteFile crée le fichier si nécessaire et le tronque s'il existe. Le troisième argument est la permission Unix d'un fichier nouvellement créé : 0o644 signifie que le propriétaire peut lire et écrire, tous les autres lire. Il est ignoré pour un fichier qui existe déjà, et l'umask du processus peut retirer des bits.
os.ReadFile lit tout en mémoire. C'est le bon choix pour des fichiers de configuration et de petites entrées, et le mauvais pour un log de plusieurs gigaoctets.
Lire ligne par ligne avec bufio.Scanner
Pour les gros fichiers, ou quand vous voulez de toute façon des lignes, utilisez bufio.Scanner. Il lit par blocs et vous donne une ligne à la fois, sans le retour à la ligne.
Trois détails :
- Vérifiez
sc.Err()après la boucle.Scanrenvoiefalseaussi bien en fin de fichier qu'en cas d'erreur, et seulErrfait la différence. - La limite de 64 Ko par ligne. Par défaut, une ligne de plus de 64 Ko arrête le scanner avec
bufio.Scanner: token too long. Pour les fichiers à longues lignes (JSON minifié, certains logs), relevez la limite avant la boucle :sc.Buffer(make([]byte, 1024*1024), 10*1024*1024). - Autres unités.
sc.Split(bufio.ScanWords)produit des mots ;bufio.ScanRunesproduit des caractères.
Pour lire un flux par blocs de taille fixe plutôt que par lignes, utilisez f.Read(buf) dans une boucle ou io.Copy vers un autre writer.
Écrire : os.Create, os.OpenFile et l'ajout en fin de fichier
os.Create(name) ouvre un fichier en écriture, en le créant ou en le tronquant. os.OpenFile donne un contrôle total grâce à des flags :
| Flag | Signification |
|---|---|
os.O_RDONLY, os.O_WRONLY, os.O_RDWR | ouvrir en lecture, en écriture, ou les deux (choisissez-en un) |
os.O_CREATE | créer le fichier s'il n'existe pas |
os.O_TRUNC | vider le fichier à l'ouverture |
os.O_APPEND | chaque écriture va à la fin |
os.O_EXCL | avec O_CREATE : échouer si le fichier existe déjà |
os.Open(name) équivaut à OpenFile(name, O_RDONLY, 0). os.Create(name) équivaut à OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0o666).
En écriture, l'erreur de Close compte. Certains systèmes de fichiers ne signalent les échecs d'écriture qu'à la fermeture, donc defer f.Close() seul peut masquer une perte de données. Pour les fichiers que vous écrivez, vérifiez Close explicitement comme le fait appendLine. Pour les fichiers que vous lisez seulement, defer f.Close() suffit.
fmt.Fprintln et les autres fonctions d'affichage de fmt acceptent n'importe quel io.Writer, y compris un fichier. Un bufio.Writer regroupe les petites écritures en mémoire. Oublier w.Flush() est un bug classique : le programme se termine normalement et les derniers kilo-octets n'atteignent jamais le fichier.
Le fichier existe-t-il ?
Go n'a pas de os.Exists. Appelez os.Stat et examinez l'erreur :
errors.Is(err, fs.ErrNotExist) est l'idiome actuel. Il remplace l'ancien os.IsNotExist(err), qui ne voit pas à travers les erreurs enveloppées.
Vérifier avant d'ouvrir est souvent inutile et sujet aux races : le fichier peut apparaître ou disparaître entre la vérification et l'ouverture. En général, vous l'ouvrez simplement et gérez le fs.ErrNotExist renvoyé par l'ouverture. Pour créer un fichier seulement s'il n'existe pas encore, utilisez O_CREATE|O_EXCL, qui fait de la vérification et de la création une seule étape atomique.
Répertoires
| Tâche | Fonction |
|---|---|
| créer un répertoire | os.Mkdir(path, 0o755) |
| créer un chemin avec ses parents | os.MkdirAll(path, 0o755) |
| lister un répertoire | os.ReadDir(path) |
| parcourir une arborescence | filepath.WalkDir(root, fn) |
| supprimer un fichier ou un répertoire vide | os.Remove(path) |
| supprimer une arborescence | os.RemoveAll(path) |
| renommer ou déplacer | os.Rename(old, new) |
| fichier ou répertoire temporaire | os.CreateTemp("", "prefix-*"), os.MkdirTemp("", "prefix") |
| assembler des morceaux de chemin | filepath.Join(a, b, c) |
Utilisez path/filepath pour les chemins du système de fichiers : il emploie le bon séparateur pour le système d'exploitation (\ sous Windows). Le package path sert aux chemins séparés par des barres obliques, comme les URL.
Go 1.24 a aussi ajouté os.Root (os.OpenRoot(dir)), qui n'ouvre des fichiers qu'à l'intérieur d'un répertoire et refuse les chemins qui en sortent via .. ou des liens symboliques. Utilisez-le quand les noms de fichiers viennent des utilisateurs.
Erreurs courantes
- Ne pas vérifier les erreurs. Chacun de ces appels peut échouer. Un fichier dont l'ouverture a échoué vaut
nil, et chaqueRead,WriteouCloseultérieur renvoieinvalid argument, ce qui masque la vraie cause (le fichier manquait, ou la permission a été refusée). - Oublier
sc.Err()après une boucle de lecture. Une erreur de lecture ressemble à une fin de fichier. - Oublier
Flushsur unbufio.Writer. La fin du fichier manque. - Ignorer l'erreur de
Closeaprès une écriture. Les erreurs d'écriture peuvent n'apparaître qu'à ce moment-là. defer f.Close()dans une boucle sur de nombreux fichiers. Les fichiers restent ouverts jusqu'au retour de la fonction et vous pouvez manquer de descripteurs de fichiers. Déplacez le corps dans une fonction pour que chaque fichier soit fermé à chaque itération.- Des permissions écrites en décimal.
644n'est pas0o644. Go lit644comme le nombre décimal, soit0o1204, ce qui positionne des bits étranges.
Questions fréquentes
Comment lire tout un fichier dans une chaîne en Go ?
data, err := os.ReadFile("notes.txt") renvoie le contenu sous forme de []byte ; convertissez avec string(data). La fonction ouvre, lit et ferme le fichier pour vous. Utilisez-la pour des fichiers qui tiennent confortablement en mémoire ; pour les gros fichiers, lisez ligne par ligne avec bufio.Scanner.
Comment lire un fichier ligne par ligne en Go ?
Ouvrez le fichier avec os.Open, faites defer f.Close(), enveloppez-le dans bufio.NewScanner(f), bouclez avec for sc.Scan() { line := sc.Text() }, et vérifiez sc.Err() après la boucle. Les lignes de plus de 64 Ko font échouer le scanner avec token too long, sauf si vous agrandissez son buffer avec sc.Buffer.
Comment ajouter du contenu à la fin d'un fichier en Go ?
Ouvrez-le avec os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644), écrivez, et vérifiez l'erreur de Close. O_CREATE crée le fichier s'il n'existe pas, et O_APPEND envoie chaque écriture à la fin.
Comment vérifier si un fichier existe en Go ?
Appelez os.Stat(path) et testez l'erreur avec errors.Is(err, fs.ErrNotExist). Une erreur nil signifie qu'il existe. Toute autre erreur (permission refusée, par exemple) signifie que vous ne pouvez pas savoir, donc traitez-la à part au lieu de la considérer comme « n'existe pas ».