Gestion des fichiers en Go
La gestion des fichiers est l'une des opérations les plus courantes en Go — la lecture de configuration, l'écriture de journaux et la persistance des données en dépendent toutes. La bibliothèque standard de Go fournit un ensemble d'interfaces d'E/S de fichiers concis mais puissant, couvrant des scénarios allant des lectures/écritures ponctuelles au traitement en flux.
Il existe 5 paquets principaux liés à la gestion des fichiers, chacun avec son propre rôle :
Remarque sur la version :
ioutilLe paquet a été déprécié dans Go 1.16, et sa fonctionnalité a été déplacée versos(tel queos.ReadFile、os.WriteFile) etio(tel queio.ReadAll). Le nouveau code doit directement utiliserosetiopaquet.
1. Création de fichier
os.CreateCréer un nouveau fichier. Si le fichier existe déjà,il sera tronqué (contenu effacé). L'objet fichier retourné doit être fermé pour libérer les ressources système :
Exemple
import (
"log"
"os"
)
func main() {
// os.Create crée un fichier (le tronque s'il existe déjà)
file, err := os.Create("test.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close() // defer garantit que le fichier est fermé lorsque la fonction se termine
// Écrire du contenu pour vérifier que la création a réussi
file.WriteString("Fichier créé avec succès\n")
log.Println("Fichier créé avec succès")
}
defer file.Close()est une bonne pratique pour les opérations sur les fichiers en Go.defergarantit que même si le code suivant déclenche une panique, le fichier sera correctement fermé, évitant les fuites de descripteurs de fichiers.
2. Ouverture et fermeture de fichiers
osLe package propose trois façons d'ouvrir des fichiers, adaptées à différents scénarios :
| Fonction | Mode | Description |
|---|---|---|
os.Open(name) |
Lecture seule | La façon la plus simple est d'ouvrir en lecture seule |
os.Create(name) |
Lecture/Écriture + Créer/Tronquer | Créer un nouveau fichier ou vider un fichier existant |
os.OpenFile(name, flag, perm) |
Personnalisé | Vous pouvez spécifier des flags tels que lecture/écriture, ajout, création, etc. |
Exemple
import (
"fmt"
"os"
)
func main() {
// Méthode 1 : ouvrir en lecture seule
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Échec de l'ouverture :, err)
return
}
defer file.Close()
fmt.Println("Fichier ouvert avec succès")
// Méthode 2 : OpenFile en mode personnalisé
// os.O_WRONLY en écriture seule
// os.O_CREATE créer s'il n'existe pas
// os.O_APPEND mode ajout
// os.O_TRUNC tronquer s'il existe
f, err := os.OpenFile("log.txt",
os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Println("Échec de l'ouverture :, err)
return
}
defer f.Close()
f.WriteString("Append a line of log\n")
}
Descriptions des indicateurs OpenFile
| Indicateurs | Description |
|---|---|
os.O_RDONLY |
Lecture seule (par défaut) |
os.O_WRONLY |
Écriture seule |
os.O_RDWR |
Lecture-écriture |
os.O_APPEND |
Mode ajout, écrit le contenu ajouté à la fin du fichier |
os.O_CREATE |
Créer si le fichier n'existe pas |
os.O_TRUNC |
Effacer le contenu du fichier à l'ouverture |
os.O_EXCL |
Utilisé avec CREATE, erreur si le fichier existe déjà |
3. Lecture de fichiers
Go propose trois méthodes principales pour lire des fichiers ; choisissez selon la taille du fichier et les exigences de traitement :
3.1 Lecture en une seule fois (recommandée pour les petits fichiers)
os.ReadFileest la méthode la plus simple — ouvre, lit et ferme automatiquement le fichier en une seule étape :
Exemple
import (
"fmt"
"log"
"os"
)
func main() {
// Lire tout le fichier en une seule fois (Go 1.16+)
data, err := os.ReadFile("config.json")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
}
3.2 Lecture ligne par ligne (recommandée pour les fichiers volumineux)
Pour les fichiers volumineux, utilisezbufio.ScannerTraitez ligne par ligne pour éviter de tout charger en mémoire d'un coup :
Exemple
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
file, err := os.Open("large-file.log")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Lire ligne par ligne
scanner := bufio.NewScanner(file)
lineNum := 0
for scanner.Scan() {
lineNum++
line := scanner.Text() // Obtenir le contenu de la ligne actuelle
fmt.Printf("Ligne %d: %s\n", lineNum, line)
}
// Vérifier si une erreur s'est produite pendant l'analyse
if err := scanner.Err(); err != nil {
log.Fatal("Erreur de lecture :", err)
}
}
Remarque :
bufio.ScannerLa longueur maximale par défaut d'une ligne est de 64KB. Si le fichier contient des lignes trop longues, vous devez, lors de l'appelScan()Préréglagescanner.Buffer(make([]byte, 0), maxSize)pour augmenter le tampon.
3.3 Lecture avec io.ReadAll
Lorsque vous avez déjà unio.Reader(comme les réponses réseau, les fichiers ouverts), vous pouvez utiliserio.ReadAll:
Exemple
import (
"fmt"
"io"
"log"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Lire toutes les données depuis Reader
data, err := io.ReadAll(file)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data))
}
4. Écriture de fichiers
Go propose plusieurs façons d'écrire, des écritures simples en une seule fois aux écritures tamponnées à hautes performances :
4.1 Écriture en une seule fois
os.WriteFileÉcrire les données dans le fichier en une seule fois (en écrasant le contenu existant) :
Exemple
import (
"log"
"os"
)
func main() {
content := []byte("Hello, Go!\nThis is the second line\n")
// 0644 : lecture/écriture pour le propriétaire, lecture seule pour les autres
err := os.WriteFile("output.txt", content, 0644)
if err != nil {
log.Fatal(err)
}
log.Println("Écriture réussie")
}
4.2 Écriture à l'aide de l'objet File
L'écriture via l'objet fichier permet d'écrire du contenu en plusieurs passes :
Exemple
import (
"fmt"
"log"
"os"
)
func main() {
file, err := os.Create("output.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Méthode 1 : Écrire une chaîne
file.WriteString("Écrire directement une chaîne\n")
// Méthode 2 : Écrire une tranche d'octets
data := []byte("Écrire une tranche d'octets\n")
file.Write(data)
// Méthode 3 : Écriture formatée
fmt.Fprintf(file, "Écriture formatée : %d + %d = %d\n", 3, 4, 7)
}
4.3 Écriture tamponnée (recommandée pour de grandes quantités de données)
bufio.WriterIl écrit d'abord les données dans un tampon en mémoire, puis les écrit sur disque par lots une fois qu'une quantité suffisante s'est accumulée, ce qui réduit considérablement le nombre d'opérations d'E/S.Veillez à appeler avant de terminerFlush():
Exemple
import (
"bufio"
"log"
"os"
)
func main() {
file, err := os.Create("buffered-output.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Créer un writer tamponné
writer := bufio.NewWriter(file)
// Écrire plusieurs lignes (passe d'abord par le tampon, pas immédiatement sur le disque)
for i := 0; i < 1000; i++ {
writer.WriteString("Ceci est la ligne " + itoa(i) + " ligne\n")
}
// Flush écrit les données tamponnées restantes dans le fichier
if err := writer.Flush(); err != nil {
log.Fatal("Échec du vidage du tampon :", err)
}
log.Println("Écriture tamponnée terminée")
}
// Fonction utilitaire simple pour convertir un int en string
func itoa(n int) string {
return fmt.Sprintf("%d", n)
}
5. Ajout à des fichiers
Utilisationos.O_APPENDLe flag ajoute du contenu à la fin du fichier sans écraser les données existantes :
Exemple
import (
"log"
"os"
"time"
)
func main() {
// O_APPEND : mode d'ajout
// O_CREATE : créer s'il n'existe pas
// O_WRONLY : écriture seule
file, err := os.OpenFile("app.log",
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Ajoute à chaque exécution, n'écrase pas
timestamp := time.Now().Format("2006-01-02 15:04:05")
file.WriteString("[" + timestamp + "] Application démarrée\n")
}
Comparaison :
os.CreateCela effacera le contenu existant ;os.OpenFileCoordinationO_APPENDCela ajoutera le contenu. Dans les scénarios de journalisation, veillez à utiliser le mode ajout.
6. Suppression et renommage de fichiers
Exemple
import (
"log"
"os"
)
func main() {
// Renommer / Déplacer le fichier
err := os.Rename("old.txt", "new.txt")
if err != nil {
log.Fatal("Échec du renommage :", err)
}
log.Println("Renommage réussi")
// Supprimer le fichier
err = os.Remove("temp.txt")
if err != nil {
log.Fatal("Échec de la suppression :", err)
}
log.Println("Suppression réussie")
}
7. Informations et inspection de fichiers
7.1 Obtention d'informations sur un fichier
Exemple
import (
"fmt"
"log"
"os"
)
func main() {
info, err := os.Stat(test.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println("Nom du fichier : ", info.Name())
fmt.Println("Taille : ", info.Size(), "bytes")
fmt.Println("Permissions : ", info.Mode())
fmt.Println("Modifié : ", info.ModTime())
fmt.Println("Est un répertoire : ", info.IsDir())
}
7.2 Vérifier si un fichier existe
Go n'a pas de fonction dédiée "file exists"; paros.Stat + os.IsNotExistDéterminer :
Exemple
import (
"fmt"
"os"
)
// fileExists vérifie si le fichier existe
func fileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
func main() {
if fileExists("test.txt") {
fmt.Println("Le fichier existe")
} else {
fmt.Println("Le fichier n'existe pas")
}
}
8. Opérations sur les répertoires
Exemple
import (
"fmt"
"log"
"os"
)
func main() {
// Créer un seul répertoire
err := os.Mkdir("newdir", 0755)
if err != nil {
log.Fatal(err)
}
// Créer récursivement des répertoires à plusieurs niveaux (crée automatiquement les répertoires parents même s'ils n'existent pas)
err = os.MkdirAll("path/to/deep/dir", 0755)
if err != nil {
log.Fatal(err)
}
// Lire le contenu du répertoire
entries, err := os.ReadDir(".")
if err != nil {
log.Fatal(err)
}
for _, entry := range entries {
mark := "📄"
if entry.IsDir() {
mark = "📁"
}
fmt.Printf("%s %s\n", mark, entry.Name())
}
// Supprimer le répertoire vide
os.Remove("newdir")
// Supprimer récursivement le répertoire et tout son contenu (⚠ à utiliser avec prudence)
os.RemoveAll("path")
}
9. Opérations avancées
9.1 Copie de fichiers
Utilisationio.CopyCopier efficacement des données entre deux fichiers :
Exemple
import (
"io"
"log"
"os"
)
// copyFile est une fonction générique pour copier des fichiers
func copyFile(src, dst string) (int64, error) {
srcFile, err := os.Open(src)
if err != nil {
return 0, err
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return 0, err
}
defer dstFile.Close()
// io.Copy gère automatiquement le tampon pour une copie efficace
return io.Copy(dstFile, srcFile)
}
func main() {
n, err := copyFile("source.txt", "destination.txt")
if err != nil {
log.Fatal(err)
}
log.Printf("Copie terminée, %d bytes au total", n)
}
9.2 Fichiers et répertoires temporaires
Exemple
import (
"fmt"
"log"
"os"
)
func main() {
// Créer un fichier temporaire (nommé automatiquement, le préfixe est "app-")
tmpFile, err := os.CreateTemp("", "app-*.txt")
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmpFile.Name()) // Nettoyer après utilisation
fmt.Println("Fichier temporaire :", tmpFile.Name())
// Créer un répertoire temporaire
tmpDir, err := os.MkdirTemp("", "app-*")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(tmpDir) // Nettoyer après utilisation
fmt.Println("Répertoire temporaire :", tmpDir)
}
9.3 Parcours récursif des répertoires
filepath.WalkVous pouvez parcourir récursivement tous les fichiers et sous-répertoires dans l’arborescence des répertoires :
Exemple
import (
"fmt"
"log"
"os"
"path/filepath"
)
func main() {
root := "." // Commencer le parcours depuis le répertoire courant
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err // Lorsqu'une erreur se produit, vous pouvez choisir d'ignorer ou de retourner
}
// Afficher le chemin et la taille
size := ""
if !info.IsDir() {
size = fmt.Sprintf(" (%d bytes)", info.Size())
}
fmt.Println(path + size)
return nil
})
if err != nil {
log.Fatal(err)
}
}
9.4 Assemblage de chemins multiplateforme
Ne jamais utiliser la concaténation de chaînes pour construire des chemins(commedir + "/" + file), utilisezfilepath.JoinPour garantir la compatibilité multiplateforme :
// 错误:Windows 上路径分隔符是 \
path := "dir" + "/" + "file.txt"
// 正确:自动适配当前操作系统
path := filepath.Join("dir", "file.txt")
Référence rapide des scénarios
| Scénarios | Approche recommandée | Code clé |
|---|---|---|
| Lire un petit fichier | os.ReadFile |
data, err := os.ReadFile("file.txt") |
| Lire de gros fichiers ligne par ligne | bufio.Scanner |
scanner := bufio.NewScanner(file) |
| Écrire un petit fichier | os.WriteFile |
os.WriteFile("f.txt", data, 0644) |
| Écrire de grandes quantités de données | bufio.Writer |
writer := bufio.NewWriter(file) |
| Ajouter au journal | os.OpenFile + O_APPEND |
os.OpenFile("log", os.O_APPEND|os.O_WRONLY, 0644) |
| Copie de fichier | io.Copy |
io.Copy(dst, src) |
| Parcourir un répertoire | filepath.Walk |
filepath.Walk(".", callback) |
| Path join | filepath.Join |
filepath.Join("dir", "file.txt") |
| Vérifier l'existence d'un fichier | os.Stat + os.IsNotExist |
_, err := os.Stat(path) |
| Créer des fichiers temporaires | os.CreateTemp |
os.CreateTemp("", "prefix-*.txt") |
autres extensionsDescription des permissions de fichier :
0644Indique que le propriétaire peut lire et écrire (6=4+2), tandis que le groupe et les autres utilisateurs sont en lecture seule (4). Les répertoires utilisent généralement0755(ajoute la permission d'exécution, sinon vous ne pouvez pas entrer dans le répertoire)