Lavorare con i File in Go
Go offre strumenti potenti e semplici per lavorare con i file attraverso i pacchetti os, bufio, io e path/filepath. La gestione dei file in Go segue il principio della semplicita, con funzioni ad alto livello per le operazioni comuni e accesso a basso livello quando necessario.
Aprire un File con os.Open
os.Open apre un file in sola lettura:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("esempio.txt")
if err != nil {
fmt.Println("Errore:", err)
return
}
defer file.Close() // IMPORTANTE: chiudere sempre il file
fmt.Println("File aperto con successo")
}
Creare un File con os.Create
os.Create crea un nuovo file (o tronca un file esistente) in modalita scrittura:
func main() {
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("Errore:", err)
return
}
defer file.Close()
file.WriteString("Ciao, mondo!\n")
file.WriteString("Scritto da Go.\n")
}
Leggere File
Lettura Completa con os.ReadFile
Il modo piu semplice per leggere un intero file in memoria:
package main
import (
"fmt"
"os"
)
func main() {
contenuto, err := os.ReadFile("esempio.txt")
if err != nil {
fmt.Println("Errore:", err)
return
}
fmt.Println(string(contenuto))
}
os.ReadFile restituisce un []byte con l’intero contenuto del file.
Lettura Riga per Riga con bufio.Scanner
Per file grandi, e meglio leggere riga per riga:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("esempio.txt")
if err != nil {
fmt.Println("Errore:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
numeroRiga := 1
for scanner.Scan() {
fmt.Printf("%d: %s\n", numeroRiga, scanner.Text())
numeroRiga++
}
if err := scanner.Err(); err != nil {
fmt.Println("Errore durante la lettura:", err)
}
}
Lettura con bufio.Reader
Per un controllo piu fine sulla lettura:
func main() {
file, err := os.Open("esempio.txt")
if err != nil {
fmt.Println("Errore:", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
for {
riga, err := reader.ReadString('\n')
if err != nil {
break
}
fmt.Print(riga)
}
}
Scrivere File
Scrittura Completa con os.WriteFile
Il modo piu semplice per scrivere un file:
func main() {
contenuto := []byte("Ciao, mondo!\nScritto con os.WriteFile.\n")
err := os.WriteFile("output.txt", contenuto, 0644)
if err != nil {
fmt.Println("Errore:", err)
return
}
fmt.Println("File scritto con successo")
}
Il terzo parametro (0644) definisce i permessi del file.
Scrittura con bufio.Writer
Per scritture bufferizzate e piu efficienti:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("Errore:", err)
return
}
defer file.Close()
writer := bufio.NewWriter(file)
writer.WriteString("Prima riga\n")
writer.WriteString("Seconda riga\n")
writer.WriteString("Terza riga\n")
// IMPORTANTE: svuotare il buffer per assicurarsi che i dati vengano scritti
writer.Flush()
}
Aggiungere a un File Esistente
Per aggiungere contenuto senza sovrascrivere:
func main() {
file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("Errore:", err)
return
}
defer file.Close()
file.WriteString("Nuova riga di log\n")
}
I flag principali di os.OpenFile:
os.O_RDONLY: sola letturaos.O_WRONLY: sola scritturaos.O_RDWR: lettura e scritturaos.O_APPEND: aggiunge alla fineos.O_CREATE: crea il file se non esisteos.O_TRUNC: tronca il file all’apertura
Permessi dei File
I permessi in Go seguono il sistema Unix (ottale):
// Permessi comuni
os.WriteFile("file.txt", dati, 0644) // rw-r--r-- (lettura per tutti, scrittura per il proprietario)
os.WriteFile("script.sh", dati, 0755) // rwxr-xr-x (eseguibile)
os.WriteFile("segreto.txt", dati, 0600) // rw------- (solo proprietario)
os.Mkdir("cartella", 0755) // rwxr-xr-x
Defer per Chiudere i File
Usare defer per chiudere i file e una best practice fondamentale in Go:
func leggiFile(percorso string) (string, error) {
file, err := os.Open(percorso)
if err != nil {
return "", err
}
defer file.Close() // garantisce la chiusura anche in caso di errore
contenuto, err := io.ReadAll(file)
if err != nil {
return "", err
}
return string(contenuto), nil
}
Lavorare con le Directory
Creare Directory
// Crea una singola directory
err := os.Mkdir("nuova_cartella", 0755)
// Crea directory annidate (come mkdir -p)
err = os.MkdirAll("percorso/annidato/profondo", 0755)
Leggere il Contenuto di una Directory
func main() {
entries, err := os.ReadDir(".")
if err != nil {
fmt.Println("Errore:", err)
return
}
for _, entry := range entries {
if entry.IsDir() {
fmt.Printf("[DIR] %s\n", entry.Name())
} else {
info, _ := entry.Info()
fmt.Printf("[FILE] %s (%d bytes)\n", entry.Name(), info.Size())
}
}
}
Rimuovere File e Directory
// Rimuove un singolo file o directory vuota
os.Remove("file.txt")
// Rimuove ricorsivamente (come rm -rf)
os.RemoveAll("cartella_da_eliminare")
Il Pacchetto filepath
Il pacchetto path/filepath fornisce funzioni per manipolare i percorsi in modo portabile:
package main
import (
"fmt"
"path/filepath"
)
func main() {
// Unire percorsi
percorso := filepath.Join("home", "utente", "documenti", "file.txt")
fmt.Println(percorso) // home/utente/documenti/file.txt
// Ottenere la directory
dir := filepath.Dir(percorso)
fmt.Println(dir) // home/utente/documenti
// Ottenere il nome del file
nome := filepath.Base(percorso)
fmt.Println(nome) // file.txt
// Ottenere l'estensione
ext := filepath.Ext(percorso)
fmt.Println(ext) // .txt
// Percorso assoluto
abs, _ := filepath.Abs("file.txt")
fmt.Println(abs)
}
Camminare in una Directory (Walk)
func main() {
filepath.WalkDir(".", func(percorso string, d os.DirEntry, err error) error {
if err != nil {
return err
}
fmt.Println(percorso)
return nil
})
}
Conclusione
Go offre un set completo di strumenti per lavorare con i file, dalla lettura e scrittura semplice con os.ReadFile/os.WriteFile, alla gestione avanzata con bufio per operazioni bufferizzate. Usare defer per chiudere i file, gestire correttamente i permessi e sfruttare il pacchetto filepath per i percorsi sono pratiche essenziali per scrivere codice Go robusto e portabile.