1. Introduzione
Lavorare direttamente con gli stream (StreamReader, StreamWriter, FileStream) è molto utile, soprattutto quando hai bisogno di controllare la lettura/scrittura a basso livello: per esempio scrivere blocchi "a pezzi" o processare file grandi a parti.
Ma nella vita reale spesso serve semplicemente sapere: "esiste questo file?", "copiare un file da una posizione a un'altra", "cancellare un file", "ottenere la lista di tutti i file .txt in una cartella", e così via. Per questi casi Microsoft ha creato due utility universali: le classi statiche File e Directory. Statiche significa che non devi creare un'istanza: chiami direttamente il metodo sul nome della classe.
Analogia pratica
Se gli stream sono te con una matita che scrive ogni lettera sul foglio, allora File e Directory sono la tua scrivania con i cassetti: puoi "aprire" istantaneamente un cassetto (Directory), tirarne fuori un foglio (File.ReadAllText), buttare via il foglio (File.Delete) o perfino prendere un intero cassetto e spostarlo dall'altra parte della stanza (Directory.Move). Compiti semplici — metodi semplici.
2. Classe File: cos'è e a cosa serve
La classe File offre un set comodo di metodi statici per le operazioni più comuni sui file.
I metodi più usati
| Metodo | Descrizione |
|---|---|
|
Controlla se esiste un file al percorso |
|
Legge l'intero file come testo |
|
Legge tutte le righe del file in un array |
|
Sovrascrive il file scrivendo il testo |
|
Aggiunge testo alla fine del file |
|
Copia un file |
|
Elimina un file |
|
Sposta o rinomina un file |
|
Apre uno stream per operazioni avanzate |
Esempio rapido: controlliamo un file e lo leggiamo
string filePath = "data.txt";
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath);
Console.WriteLine("Contenuto del file:");
Console.WriteLine(content);
}
else
{
Console.WriteLine("File non trovato!");
}
Vedi? Nessuna gestione manuale degli stream per il caso semplice!
3. Classe Directory: gestire le cartelle senza dolore
Se File è per i singoli file, allora Directory è per le cartelle (directory).
Compiti principali
| Metodo | Descrizione |
|---|---|
|
Controlla se la cartella esiste |
|
Crea la cartella (o l'intero percorso con le cartelle intermedie) |
|
Elimina la cartella (può includere sottodirectory) |
|
Ottiene la lista dei file nella cartella |
|
Ottiene la lista delle sottodirectory |
|
Sposta o rinomina la cartella |
|
Ottiene la directory di lavoro corrente dell'applicazione |
Esempio: creiamo una cartella e salviamo lì un file
string dirPath = "Results";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
Console.WriteLine("Cartella 'Results' creata.");
}
string filePath = Path.Combine(dirPath, "summary.txt");
File.WriteAllText(filePath, "Dati finali: ...");
Console.WriteLine($"File {filePath} scritto.");
4. Scenari tipici d'uso di File e Directory
I metodi base possono sembrare noiosi finché non li vedi all'opera! Vediamo come usare queste classi per risolvere problemi reali e, nel frattempo, continuiamo lo sviluppo della nostra app di esempio (immaginiamo una semplice todo-list che salva i risultati su file).
Salvare i dati su file (sostituendo il file precedente)
// Salviamo la lista di task su file
string[] todos = { "Comprare il pane", "Chiamare il medico", "Fare i compiti di C#" };
string filePath = "todo.txt";
File.WriteAllLines(filePath, todos);
Console.WriteLine("Lista delle cose da fare salvata.");
Caricare i dati dal file
// Carichiamo i task, se il file esiste
if (File.Exists(filePath))
{
string[] loadedTodos = File.ReadAllLines(filePath);
Console.WriteLine("La tua lista di cose da fare:");
foreach (var task in loadedTodos)
{
Console.WriteLine("- " + task);
}
}
else
{
Console.WriteLine("Non hai ancora una lista di cose da fare.");
}
Aggiungere un nuovo task (senza sovrascrivere il file)
string newTask = "Portare a passeggio il cane";
File.AppendAllText(filePath, newTask + Environment.NewLine);
Console.WriteLine("Task aggiunto!");
Copiare e fare un backup del file
string backupPath = "todo_backup.txt";
File.Copy(filePath, backupPath, overwrite: true);
Console.WriteLine("Backup della lista delle cose da fare creato.");
Spostare (o rinominare) il file
string archivePath = "todo_archive.txt";
File.Move(filePath, archivePath);
Console.WriteLine("Lista delle cose da fare archiviata (rinominata).");
Cancellare il file
if (File.Exists(archivePath))
{
File.Delete(archivePath);
Console.WriteLine("Archivio eliminato — liberiamo spazio!");
}
5. Lavorare con le cartelle: esempi
Creare una gerarchia di directory
string path = Path.Combine("Reports", "2024", "June");
Directory.CreateDirectory(path);
Console.WriteLine($"Cartella {path} creata (incluse le cartelle intermedie).");
Ottenere la lista di file e cartelle
string dirPath = "Reports";
if (Directory.Exists(dirPath))
{
string[] files = Directory.GetFiles(dirPath);
Console.WriteLine("File nella cartella:");
foreach (var file in files)
{
Console.WriteLine(file);
}
string[] subDirs = Directory.GetDirectories(dirPath);
Console.WriteLine("Sottodirectory:");
foreach (var dir in subDirs)
{
Console.WriteLine(dir);
}
}
Filtrare i file per pattern (solo txt)
string[] txtFiles = Directory.GetFiles(dirPath, "*.txt");
Console.WriteLine("Solo file .txt:");
foreach (var file in txtFiles)
{
Console.WriteLine(file);
}
Scansione ricorsiva (file in tutte le sottocartelle)
string[] allFiles = Directory.GetFiles(dirPath, "*.*", SearchOption.AllDirectories);
Console.WriteLine("File in tutte le cartelle annidate:");
foreach (var file in allFiles)
{
Console.WriteLine(file);
}
Spostare ed eliminare directory
string from = "OldReports";
string to = "Archive/OldReports";
if (Directory.Exists(from))
{
Directory.Move(from, to);
Console.WriteLine($"Cartella {from} spostata in {to}");
}
if (Directory.Exists(to))
{
Directory.Delete(to, recursive: true); // true: cancellare tutto dentro
Console.WriteLine($"Cartella {to} eliminata (con tutto il contenuto).");
}
6. Note utili
Integrazione con i percorsi: la classe Path
Quando lavori con file e cartelle spesso devi costruire percorsi completi, unire nomi di directory e file, estrarre estensioni, verificare caratteri validi. Per questo si usa la classe helper Path.
Esempi
string folder = "Results";
string filename = "week1.txt";
string fullPath = Path.Combine(folder, filename); // unione sicura!
Console.WriteLine(fullPath); // "Results/week1.txt" (o "\" su Windows)
- Ottenere l'estensione del file: Path.GetExtension(fullPath)
- Ottenere il nome del file senza il percorso: Path.GetFileName(fullPath)
Usa Path.Combine invece di concatenare stringhe — è più sicuro per applicazioni cross-platform.
Metodi "tutto in una volta" vs variante a stream
Molti metodi di File e Directory (per esempio File.ReadAllText, File.WriteAllText, Directory.GetFiles) fanno il lavoro "tutto in una volta": leggono/scrivono/ottengono la lista in una singola operazione. Questo è MOLTO comodo, ma non è adatto per file o directory enormi dove la memoria può finire. Se lavori con gigabyte — passa agli stream (StreamReader, StreamWriter, FileStream, ecc.) o processa i file a pezzi.
Quando usare File/Directory e quando le classi degli stream
| Situazione | Cosa usare |
|---|---|
| Devi verificare se esiste un file/cartella | |
| Leggere/scrivere un file piccolo tutto insieme | |
| Aggiungere testo alla fine del file | |
| Ottenere la lista di file in una directory | |
| Copiare, eliminare, spostare un file | |
| Copiare/eliminare un'intera cartella | Directory.Delete/Move/Copy (Copy — con libreria esterna) |
| Processare FILE MOLTO grandi a pezzi | Classi a stream (StreamReader, FileStream) |
Valore pratico e applicazioni reali
- Caricamento/salvataggio rapido di configurazioni, impostazioni, file JSON/XML.
- Sistemi di logging (scrittura di log su disco).
- Utility semplici di backup e migrazione dati.
- Scansione di cartelle multimedia, foto, documenti.
- Scenari utente: caricamento template, export/import, generazione report ecc.
Anche nelle interviste tecniche spesso ti chiedono di usare proprio queste classi: "Implementa una funzione che calcola la dimensione totale di tutti i file .txt in una directory", "Fai il backup di file in una cartella separata" e simili.
Cheat-sheet visivo: quale metodo usare
graph TD
A[Che vuoi fare?]
A -->|Verificare esistenza| B[File.Exists o Directory.Exists]
A -->|Leggere/scrivere file completamente| C[File.ReadAllText/WriteAllText]
A -->|Aggiungere dati a un file| D[File.AppendAllText]
A -->|Lavorare con cartelle| E[Directory.CreateDirectory, GetFiles, GetDirectories]
A -->|Copiare/eliminare/spostare file| F[File.Copy/Delete/Move]
A -->|Copiare/eliminare cartella intera| G[Directory.Delete/Move]
7. Caratteristiche e errori tipici
Controllo dell'esistenza e "race condition". Anche se verifichi che il file/cartella esiste, tra quel controllo e l'operazione reale un altro processo potrebbe cambiarne lo stato. Perciò usa sempre try-catch anche se hai già verificato!
Permessi di accesso. Se l'app non ha permessi di lettura/scrittura/cancellazione, i metodi lanceranno UnauthorizedAccessException.
Percorso troppo lungo. La lunghezza massima del percorso (di solito 260 caratteri su vecchi Windows; in .NET 9 le limitazioni sono più morbide, ma stai comunque attento).
Gestione delle eccezioni. Metodi come File.ReadAllText o Directory.GetFiles non perdonano errori: se il file/cartella non esiste — lanciano subito un'eccezione. Avvolgi in try-catch oppure verifica prima l'esistenza.
GO TO FULL VERSION