Lavorare con i File
Rust offre un modulo std::fs completo per lavorare con il file system. Le operazioni sui file sono sicure grazie al sistema dei tipi e alla gestione esplicita degli errori con Result.
Aprire un File con File::open
File::open apre un file in sola lettura:
use std::fs::File;
use std::io::Read;
fn main() -> std::io::Result<()> {
let mut file = File::open("esempio.txt")?;
let mut contenuto = String::new();
file.read_to_string(&mut contenuto)?;
println!("Contenuto:\n{}", contenuto);
Ok(())
}
Creare un File con File::create
File::create crea un nuovo file o sovrascrive quello esistente:
use std::fs::File;
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut file = File::create("output.txt")?;
file.write_all(b"Prima riga\n")?;
file.write_all(b"Seconda riga\n")?;
writeln!(file, "Terza riga con numero: {}", 42)?;
println!("File creato con successo!");
Ok(())
}
Lettura Rapida con fs::read_to_string
Per leggere un intero file in una stringa, la funzione piu comoda è fs::read_to_string:
use std::fs;
fn main() -> std::io::Result<()> {
let contenuto = fs::read_to_string("config.txt")?;
println!("{}", contenuto);
// Per leggere come byte
let bytes = fs::read("immagine.png")?;
println!("Dimensione: {} byte", bytes.len());
Ok(())
}
Lettura Bufferizzata con BufReader
Per file grandi, BufReader è piu efficiente perché legge a blocchi:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> {
let file = File::open("log.txt")?;
let reader = BufReader::new(file);
for (numero, riga) in reader.lines().enumerate() {
let riga = riga?;
println!("{:4}: {}", numero + 1, riga);
}
Ok(())
}
Scrittura Bufferizzata con BufWriter
BufWriter accumula le scritture in un buffer prima di inviarle al disco:
use std::fs::File;
use std::io::{BufWriter, Write};
fn main() -> std::io::Result<()> {
let file = File::create("dati.csv")?;
let mut writer = BufWriter::new(file);
writeln!(writer, "nome,eta,citta")?;
writeln!(writer, "Mario,30,Roma")?;
writeln!(writer, "Giulia,25,Milano")?;
writeln!(writer, "Luca,35,Napoli")?;
writer.flush()?;
println!("CSV scritto con successo!");
Ok(())
}
Append su File Esistente
Per aggiungere contenuto senza sovrascrivere, si usa OpenOptions:
use std::fs::OpenOptions;
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut file = OpenOptions::new()
.append(true)
.create(true) // crea il file se non esiste
.open("log.txt")?;
writeln!(file, "Nuova voce di log: operazione completata")?;
Ok(())
}
Scrittura Rapida con fs::write
Per scrivere rapidamente un contenuto in un file:
use std::fs;
fn main() -> std::io::Result<()> {
// Scrive una stringa
fs::write("nota.txt", "Contenuto della nota")?;
// Scrive dei byte
fs::write("dati.bin", &[0u8, 1, 2, 3, 4])?;
Ok(())
}
Metadati dei File
Si possono ottenere informazioni su un file tramite i metadati:
use std::fs;
fn main() -> std::io::Result<()> {
let metadata = fs::metadata("esempio.txt")?;
println!("Tipo: {}", if metadata.is_file() { "file" } else { "directory" });
println!("Dimensione: {} byte", metadata.len());
println!("Sola lettura: {}", metadata.permissions().readonly());
if let Ok(modificato) = metadata.modified() {
println!("Ultima modifica: {:?}", modificato);
}
Ok(())
}
Creare e Rimuovere Directory
Il modulo fs offre funzioni per gestire le directory:
use std::fs;
fn main() -> std::io::Result<()> {
// Crea una singola directory
fs::create_dir("nuova_cartella")?;
// Crea directory annidate (come mkdir -p)
fs::create_dir_all("progetto/src/moduli")?;
// Elenca il contenuto di una directory
for entry in fs::read_dir(".")? {
let entry = entry?;
let tipo = if entry.file_type()?.is_dir() { "DIR" } else { "FILE" };
println!("[{}] {}", tipo, entry.file_name().to_string_lossy());
}
// Rimuovi una directory vuota
fs::remove_dir("nuova_cartella")?;
// Rimuovi una directory e tutto il suo contenuto
fs::remove_dir_all("progetto")?;
Ok(())
}
Altre Operazioni su File
use std::fs;
fn main() -> std::io::Result<()> {
// Copiare un file
fs::copy("originale.txt", "copia.txt")?;
// Rinominare o spostare un file
fs::rename("vecchio_nome.txt", "nuovo_nome.txt")?;
// Eliminare un file
fs::remove_file("da_eliminare.txt")?;
Ok(())
}
Path e PathBuf
Path e PathBuf gestiscono i percorsi in modo cross-platform:
use std::path::{Path, PathBuf};
fn main() {
// Path è un riferimento (come &str)
let percorso = Path::new("/home/utente/documenti/file.txt");
println!("Nome file: {:?}", percorso.file_name());
println!("Estensione: {:?}", percorso.extension());
println!("Directory padre: {:?}", percorso.parent());
println!("Esiste: {}", percorso.exists());
println!("È assoluto: {}", percorso.is_absolute());
// PathBuf è posseduto (come String)
let mut percorso_buf = PathBuf::from("/home/utente");
percorso_buf.push("progetti");
percorso_buf.push("rust");
percorso_buf.set_extension("rs");
println!("Percorso costruito: {}", percorso_buf.display());
// Iterare sulle componenti del percorso
for componente in percorso.components() {
println!(" {:?}", componente);
}
// Unire percorsi con join
let base = Path::new("/var/log");
let completo = base.join("app").join("errori.log");
println!("Percorso completo: {}", completo.display());
}
Esempio Completo: Elaborazione di File
use std::fs;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::Path;
fn conta_righe(percorso: &Path) -> std::io::Result<usize> {
let file = fs::File::open(percorso)?;
let reader = BufReader::new(file);
Ok(reader.lines().count())
}
fn main() -> std::io::Result<()> {
let cartella = Path::new(".");
for entry in fs::read_dir(cartella)? {
let entry = entry?;
let percorso = entry.path();
if percorso.extension().map_or(false, |e| e == "txt") {
let righe = conta_righe(&percorso)?;
println!("{}: {} righe", percorso.display(), righe);
}
}
Ok(())
}
Conclusione
Il modulo std::fs di Rust offre un’interfaccia completa per lavorare con file e directory. File::open e File::create gestiscono l’apertura e la creazione, BufReader e BufWriter ottimizzano le prestazioni, e Path/PathBuf garantiscono la gestione cross-platform dei percorsi. Grazie al tipo Result, ogni operazione rende esplicito il possibile fallimento, portando a codice robusto e affidabile.