1. Introduzione
Quando lavori con i file tramite FileStream o altre classi di stream, è super importante capire in anticipo cosa succederà al file quando provi ad aprirlo. Spesso capita che il file esista già e vuoi aggiungere nuovi dati senza perdere quelli vecchi, oppure vuoi sovrascrivere tutto il file. Altre volte invece il file non esiste ancora e vuoi solo leggerlo, senza rischiare di creare per sbaglio un file vuoto.
Per esempio, se stai scrivendo un blocco note per appunti giornalieri, il tuo obiettivo è aggiungere nuove note in fondo a un file già esistente, mantenendo tutto quello che c'era prima. Quindi devi aprire il file in modalità aggiunta. In un altro caso — tipo se hai un file di log che deve essere creato da zero ogni volta che l'app parte, così i vecchi dati non danno fastidio. Qui devi aprire il file in modo che venga svuotato o creato da capo se non esiste. E se vuoi solo leggere un file esistente, probabilmente vuoi essere sicuro di non creare un nuovo file se non c'è, altrimenti ti ritrovi con un file vuoto dove non dovrebbe esserci.
Tutte queste situazioni diverse — aggiunta, sovrascrittura, lettura senza creazione — in C# si gestiscono tramite modalità speciali di apertura dei file, che si impostano con l'enumerazione System.IO.FileMode. Ogni modalità controlla il comportamento quando apri o crei un file, aiutandoti a gestire esattamente cosa succede quando ci lavori.
2. Enumerazione FileMode — tutte le opzioni spiegate easy
Ecco i valori principali dell'enumerazione FileMode:
| Valore | Descrizione | Comportamento se il file esiste | Comportamento se il file NON esiste |
|---|---|---|---|
|
Crea un nuovo file. Se il file esiste già — lancia un'eccezione | Errore | Ok, file creato |
|
Crea un nuovo file se non c'è, oppure sovrascrive quello esistente | Sovrascrive | Ok, crea |
|
Apre un file esistente. Se il file non c'è — eccezione | Ok, apre | Errore |
|
Apre se c'è, se no — crea nuovo | Ok, apre | Ok, crea |
|
Apre un file esistente e azzera la sua lunghezza (contenuto eliminato) | Ok, svuota il file | Errore |
|
Apre il file per aggiungere in fondo, se non c'è — crea nuovo | Ok, aggiunge in fondo | Ok, crea |
Illustrazione: Scenari di comportamento
flowchart TD
Start[Inizio]
A{Il file esiste?}
B1[Apri per aggiungere]
B2[Crea nuovo file]
B3[Sovrascrivi file]
B4[Apri file]
B5[Errore: file non trovato]
B6[Errore: file già esistente]
Start --> A
A --Append, OpenOrCreate--> B1
A --Create, Truncate--> B3
A --Open, Truncate--> B4
A --CreateNew--> B6
A --Create, OpenOrCreate, Append--> B2
A --Open, Truncate--> B5
3. Esempi di apertura file in diverse modalità
Vediamo tutto questo in pratica con la nostra mini-app che stiamo sviluppando durante il corso. Supponiamo di avere un file di testo dove scriviamo saluti e la data della richiesta. Vediamo tre scenari tipici:
- A. Creiamo un nuovo file (CreateNew)
- B. Sovrascriviamo il file (Create)
- C. Aggiungiamo righe in fondo al file (Append)
Creazione di un nuovo file (FileMode.CreateNew)
Se il file esiste già, questa modalità garantisce che non potrai crearlo di nuovo: il programma lancerà un'eccezione IOException. Perfetto se non vuoi perdere dati o cancellare file di altri per sbaglio.
using System;
using System.IO;
string filePath = "greeting.txt";
// Proviamo a creare un nuovo file — se esiste già, scatta l'eccezione!
try
{
var stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("Ciao! Questa è la prima riga.");
writer.WriteLine($"Data: {DateTime.Now}");
Console.WriteLine("File creato con successo.");
writer.Close();
}
catch (IOException)
{
Console.WriteLine($"Il file '{filePath}' esiste già! Non lo sovrascriviamo.");
}
Nota bene: se lanci questo codice una seconda volta — scatterà il catch, i dati non verranno cancellati.
Sovrascrittura del file (FileMode.Create)
Questa modalità non fa complimenti: se il file esiste, viene completamente svuotato. Se non c'è — viene creato nuovo. Comodo quando vuoi sempre partire "da zero". Tipo per risalvare un report.
using System;
using System.IO;
string filePath = "greeting.txt";
// Creazione o sovrascrittura totale del file
var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("Ciao! Ho aggiornato completamente il file :)");
writer.WriteLine($"Aggiornato: {DateTime.Now}");
Console.WriteLine("File creato o sovrascritto.");
writer.Close();
Lancia questo codice più volte — nel file ci saranno sempre solo le ultime righe aggiunte. Tutto il vecchio sparisce senza lasciare traccia.
Aggiunta in fondo al file (FileMode.Append)
Modalità top per log, registri eventi, presenze ecc. Se il file non c'è — viene creato. Se c'è — i dati si aggiungono in fondo.
using System;
using System.IO;
string filePath = "greeting.txt";
// Aggiunta di una nuova riga in fondo al file
var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine($"Un altro saluto! Data: {DateTime.Now}");
Console.WriteLine("Aggiunta una nuova riga in fondo al file.");
writer.Close();
Prova a lanciare questo codice più volte di fila. Il file crescerà, ogni nuova riga finirà in fondo.
4. Come scegliere la modalità di apertura file? Consigli pratici
Scenari reali:
- Logging: quasi sempre usa Append. Perdere i log per una sovrascrittura — dolore per l'admin e regalo per il cattivo.
- Esportazione report: di solito si usa Create, così non riempi la cartella di vecchie versioni.
- Esportazione dati critici: qui va bene CreateNew. Se il file c'è — errore, non tocchiamo nulla.
- Lettura file: qui non serve nessuna modalità di creazione, usa FileMode.Open — se il file non c'è, è un segnale di problema.
Quando scrivi codice in C# spesso usi i metodi statici della classe File, che dentro scelgono già la modalità giusta. Per esempio, File.AppendAllText lavora sempre con Append, mentre File.WriteAllText usa Create.
5. Modalità file e problemi comuni
Un po' di trappole:
Se apri un file con FileMode.Create o FileMode.Truncate, tutto il contenuto verrà cancellato. Occhio — la perdita accidentale di dati capita sia agli studenti che ai programmatori esperti.
Se provi ad aprire un file che non esiste con FileMode.Open o FileMode.Truncate, otterrai FileNotFoundException. Ricordati di controllare che il file esista davvero con File.Exists(path).
Con la modalità Append puoi solo scrivere. Se vuoi sia leggere che scrivere, guarda altre modalità (OpenOrCreate + il giusto FileAccess).
Esempio: Lettura e scrittura
A volte vuoi sia leggere che scrivere su un file (tipo hai aperto un conto in banca — vuoi versare soldi e controllare il saldo). Usa FileMode.OpenOrCreate:
using System.IO;
string filePath = "balance.txt";
// Apertura file per lettura e scrittura; se non c'è — crea
var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
// Qui puoi sia leggere che scrivere
6. Dettagli utili
Modalità di apertura file
graph TD
A[Richiedi file] --> B[Scegli modalità]
B --> C1[CreateNew] -->|Se il file esiste| D1[Errore]
C1 -->|Il file non esiste| E1[Crea]
B --> C2[Create] -->|Sempre| F1[Crea/Sovrascrivi]
B --> C3[Open] -->|Il file esiste| G1[Apri]
C3 -->|Il file non esiste| D1
B --> C4[OpenOrCreate] -->|Il file esiste| G1
C4 -->|Il file non esiste| E1
B --> C5[Append] -->|Il file esiste| H1[Apri per aggiungere]
C5 -->|Il file non esiste| E1
B --> C6[Truncate] -->|Il file esiste| I1[Svuota]
C6 -->|Il file non esiste| D1
Tabella riassuntiva delle modalità di apertura
| FileMode | Il file esiste | Il file NON esiste | Accesso ai dati | Elimina il vecchio contenuto? |
|---|---|---|---|---|
|
Eccezione | Crea nuovo | Scrive | N/D |
|
Sovrascrive | Crea nuovo | Scrive | Sì |
|
Apre | Eccezione | Legge/scrive | No |
|
Apre | Crea nuovo | Legge/scrive | No |
|
Svuota | Eccezione | Scrive | Sì |
|
Apre per scrivere in fondo | Crea nuovo | Solo scrive | No (aggiunge) |
Confronto con i metodi di alto livello della classe File
- File.WriteAllText(path, text) → dentro usa FileMode.Create, quindi il vecchio file verrà sovrascritto.
- File.AppendAllText(path, text) → usa la modalità Append, tutto viene aggiunto in fondo.
- File.ReadAllText(path) → apre il file in lettura (FileMode.Open).
A volte questi metodi bastano, ma lavorare con FileStream e specificare la modalità ti dà il pieno controllo su quello che succede.
Particolarità per diversi sistemi operativi
Spesso gli sviluppatori non considerano che su Windows un file aperto da un altro processo in scrittura può essere inaccessibile per una nuova apertura — il tentativo darà errore. Quindi è importante usare bene il parametro FileShare per gestire l'accesso condiviso al file (ne parleremo meglio in una delle prossime lezioni). Nei diversi sistemi operativi i meccanismi di lock e permessi possono cambiare, ma il concetto di modalità di apertura file resta universale.
GO TO FULL VERSION