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 :

Go File Handling Core Packages os Core: create, read/write, delete, permissions io Generic Reader/Writer Interface bufio Buffered read/write, performance optimization filepath Cross-platform path handling ioutil ⚠ Deprecated in Go 1.16 → os / io Typical usage flow os.Open() bufio.Scanner Process data defer Close()

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

package main

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

package main

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 :

Three ways to read files Read all at once os.ReadFile("file.txt") Suitable for small files (config, JSON, etc.) Automatically opens/closes, simplest Line-by-line reading bufio.NewScanner(file) Suitable for large files (logs, CSV, etc.) Memory-friendly, processes line by line Streaming read io.ReadAll(reader) Suitable for network responses and already-open files Generic Reader interface

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

package main

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

package main

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

package main

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

package main

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

package main

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

package main

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

package main

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

package main

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

package main

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

package main

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

package main

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 := "&#x1f4c4;"
                if entry.IsDir() {
                        mark = "&#x1f4c1;"
                }
                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 (&#x26a0; à 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

package main

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

package main

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

package main

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")

Description 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)

autres extensions