File I/O in C#
Impara a leggere e scrivere file: testo, CSV, JSON e gestione percorsi
🎯 Cosa Sono le Operazioni I/O?
I/O (Input/Output) si riferisce alle operazioni di lettura e scrittura dati.
In particolare, il File I/O permette ai tuoi programmi di salvare dati permanentemente
su disco e recuperarli successivamente.
💡 Perché È Importante?
Senza File I/O, tutti i dati del tuo programma scomparirebbero alla chiusura!
Immagina un’applicazione che gestisce studenti: devi poter salvare
i dati su file e caricarli al riavvio. File I/O rende i dati persistenti.
Salvare impostazioni app
Esportare/importare informazioni
Tracciare eventi e errori
Generare file CSV/JSON
Tutte le operazioni I/O richiedono il namespace System.IO:
using System; using System.IO; // ← Necessario per File I/O using System.Collections.Generic;
📄 La Classe File
La classe File fornisce metodi statici per operazioni semplici e complete su file.
Metodi per scrivere testo su file in un colpo solo.
using System;
using System.IO;
class Program
{
static void Main()
{
// Scrive tutto il testo in un file
// Se il file esiste, viene sovrascritto
string contenuto = "Questa è la prima riga.\nQuesta è la seconda riga.";
File.WriteAllText("messaggio.txt", contenuto);
Console.WriteLine("File creato con successo!");
}
}
// Scrive un array di stringhe, una per riga
string[] righe = new string[]
{
"Mario Rossi,28",
"Anna Verdi,30",
"Luca Bianchi,25"
};
File.WriteAllLines("studenti.csv", righe);
// File risultante:
// Mario Rossi,28
// Anna Verdi,30
// Luca Bianchi,25
Metodi per leggere contenuto file.
// Legge tutto il contenuto come una singola stringa
string contenuto = File.ReadAllText("messaggio.txt");
Console.WriteLine(contenuto);
// Output:
// Questa è la prima riga.
// Questa è la seconda riga.
// Legge il file e restituisce array di stringhe (una per riga)
string[] righe = File.ReadAllLines("studenti.csv");
foreach (string riga in righe)
{
Console.WriteLine(riga);
}
// Output:
// Mario Rossi,28
// Anna Verdi,30
// Luca Bianchi,25
Append invece di sovrascrivere.
// Aggiunge testo alla fine del file (non sovrascrive)
File.AppendAllText("log.txt", $"[{DateTime.Now}] Programma avviato\n");
File.AppendAllText("log.txt", $"[{DateTime.Now}] Utente connesso\n");
// File log.txt:
// [2026-01-01 10:30:00] Programma avviato
// [2026-01-01 10:30:05] Utente connesso
Verificare esistenza, copiare, eliminare, spostare.
// Verifica se file esiste
if (File.Exists("dati.txt"))
{
Console.WriteLine("Il file esiste!");
}
else
{
Console.WriteLine("File non trovato");
}
// Copia file
File.Copy("originale.txt", "copia.txt");
// Copia con sovrascrittura
File.Copy("originale.txt", "copia.txt", overwrite: true);
// Sposta (taglia e incolla)
File.Move("vecchio.txt", "nuovo.txt");
// Elimina
File.Delete("temp.txt");
// Ottieni informazioni
FileInfo info = new FileInfo("documento.txt");
Console.WriteLine($"Dimensione: {info.Length} bytes");
Console.WriteLine($"Creato: {info.CreationTime}");
Console.WriteLine($"Ultima modifica: {info.LastWriteTime}");
| Metodo | Descrizione | Quando Usarlo |
|---|---|---|
WriteAllText() |
Scrive stringa su file | Contenuto piccolo, sovrascrittura OK |
WriteAllLines() |
Scrive array di stringhe | Dati strutturati a righe |
ReadAllText() |
Legge tutto in una stringa | File piccoli, contenuto unificato |
ReadAllLines() |
Legge in array di stringhe | Elaborazione riga per riga |
AppendAllText() |
Aggiunge testo alla fine | Log, aggiornamenti incrementali |
Exists() |
Verifica esistenza | Prima di leggere/scrivere |
Copy() |
Copia file | Backup, duplicazione |
Delete() |
Elimina file | Pulizia, rimozione temp |
📝 StreamReader e StreamWriter
Per file grandi o quando serve più controllo, usa
StreamReader (lettura) e StreamWriter (scrittura).
💡 File vs StreamWriter/Reader
- Operazioni semplici e veloci
- Tutto in memoria contemporaneamente
- Ottimo per file piccoli
- Controllo granulare
- Lettura/scrittura progressiva
- Ottimo per file grandi
Scrive su file riga per riga.
using (StreamWriter writer = new StreamWriter("output.txt"))
{
writer.WriteLine("Prima riga");
writer.WriteLine("Seconda riga");
writer.Write("Terza "); // Senza a capo
writer.Write("riga");
}
// File risultante:
// Prima riga
// Seconda riga
// Terza riga
// append: true → aggiunge invece di sovrascrivere
using (StreamWriter writer = new StreamWriter("log.txt", append: true))
{
writer.WriteLine($"[{DateTime.Now}] Evento registrato");
}
// Ogni esecuzione aggiunge una riga al file esistente
Legge file riga per riga o carattere per carattere.
using (StreamReader reader = new StreamReader("dati.txt"))
{
string riga;
int numeroRiga = 1;
// Legge finché ci sono righe
while ((riga = reader.ReadLine()) != null)
{
Console.WriteLine($"Riga {numeroRiga}: {riga}");
numeroRiga++;
}
}
using (StreamReader reader = new StreamReader("documento.txt"))
{
// Legge tutto il contenuto in una volta
string tuttoIlContenuto = reader.ReadToEnd();
Console.WriteLine(tuttoIlContenuto);
}
Usa sempre using con Stream per chiudere automaticamente
il file, anche in caso di eccezioni!
// ❌ MALE: Rischio file non chiuso
StreamWriter writer = new StreamWriter("file.txt");
writer.WriteLine("testo");
writer.Close(); // Se c'è eccezione prima, non viene eseguito!
// ✅ BENE: File sempre chiuso
using (StreamWriter writer = new StreamWriter("file.txt"))
{
writer.WriteLine("testo");
// Close() automatico anche se c'è eccezione
}
🗂️ Gestione Percorsi e Directory
La classe Path aiuta a lavorare con percorsi file in modo cross-platform.
using System.IO;
// Combinare percorsi (gestisce separatori automaticamente)
string percorso = Path.Combine("C:\\Documenti", "file.txt");
// Windows: C:\Documenti\file.txt
// Linux: C:/Documenti/file.txt
// Ottenere informazioni
string nomeFile = Path.GetFileName("C:\\Dati\\report.pdf"); // "report.pdf"
string estensione = Path.GetExtension("report.pdf"); // ".pdf"
string nomeSenzaEst = Path.GetFileNameWithoutExtension("report.pdf"); // "report"
string directory = Path.GetDirectoryName("C:\\Dati\\report.pdf"); // "C:\Dati"
// Percorsi speciali
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string documentiPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
// Percorso corrente
string currentDir = Directory.GetCurrentDirectory();
// Cambiare estensione
string nuovoFile = Path.ChangeExtension("documento.txt", ".pdf"); // "documento.pdf"
using System.IO;
// Verifica se directory esiste
if (Directory.Exists("C:\\Dati"))
{
Console.WriteLine("La cartella esiste");
}
// Crea directory
Directory.CreateDirectory("C:\\NuovaCartella");
// Crea directory annidate (anche se padre non esiste)
Directory.CreateDirectory("C:\\Progetti\\CSharp\\App1");
// Ottieni tutti i file in una directory
string[] files = Directory.GetFiles("C:\\Dati");
foreach (string file in files)
{
Console.WriteLine(file);
}
// Ottieni file con pattern
string[] csvFiles = Directory.GetFiles("C:\\Dati", "*.csv");
string[] txtFiles = Directory.GetFiles("C:\\Dati", "*.txt");
// Ottieni sottodirectory
string[] subdirs = Directory.GetDirectories("C:\\Progetti");
// Elimina directory vuota
Directory.Delete("C:\\TempFolder");
// Elimina directory e contenuto
Directory.Delete("C:\\TempFolder", recursive: true);
Usa percorsi relativi invece di assoluti per portabilità:
// ❌ MALE: Percorso assoluto (non portabile)
File.WriteAllText("C:\\Users\\Mario\\Desktop\\file.txt", "dati");
// ✅ BENE: Percorso relativo (portabile)
File.WriteAllText("dati.txt", "dati"); // Nella directory corrente
// ✅ MEGLIO: Combina percorsi
string percorso = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"MiaApp",
"dati.txt"
);
Directory.CreateDirectory(Path.GetDirectoryName(percorso));
File.WriteAllText(percorso, "dati");
📊 Lavorare con File CSV
I file CSV (Comma-Separated Values) sono molto usati per esportare/importare dati.
Ogni riga è un record, i campi sono separati da virgole (o punti e virgola).
using System;
using System.IO;
using System.Collections.Generic;
class Studente
{
public string Nome { get; set; }
public string Cognome { get; set; }
public int Matricola { get; set; }
public double Media { get; set; }
}
class Program
{
static void Main()
{
List studenti = new List
{
new Studente { Nome = "Mario", Cognome = "Rossi", Matricola = 12345, Media = 27.5 },
new Studente { Nome = "Anna", Cognome = "Verdi", Matricola = 12346, Media = 28.2 },
new Studente { Nome = "Luca", Cognome = "Bianchi", Matricola = 12347, Media = 26.8 }
};
EsportaCSV(studenti, "studenti.csv");
Console.WriteLine("CSV creato con successo!");
}
static void EsportaCSV(List studenti, string percorso)
{
using (StreamWriter writer = new StreamWriter(percorso))
{
// Header (intestazioni colonne)
writer.WriteLine("Nome;Cognome;Matricola;Media");
// Dati
foreach (Studente s in studenti)
{
writer.WriteLine($"{s.Nome};{s.Cognome};{s.Matricola};{s.Media}");
}
}
}
}
// File studenti.csv risultante:
// Nome;Cognome;Matricola;Media
// Mario;Rossi;12345;27.5
// Anna;Verdi;12346;28.2
// Luca;Bianchi;12347;26.8
static ListImportaCSV(string percorso) { List studenti = new List (); using (StreamReader reader = new StreamReader(percorso)) { // Salta header (prima riga) string header = reader.ReadLine(); string riga; while ((riga = reader.ReadLine()) != null) { // Split su punto e virgola string[] campi = riga.Split(';'); if (campi.Length == 4) { Studente s = new Studente { Nome = campi[0], Cognome = campi[1], Matricola = int.Parse(campi[2]), Media = double.Parse(campi[3]) }; studenti.Add(s); } } } return studenti; } // Utilizzo List studenti = ImportaCSV("studenti.csv"); foreach (Studente s in studenti) { Console.WriteLine($"{s.Nome} {s.Cognome} - Media: {s.Media}"); }
I file CSV possono avere valori mancanti o formati errati. Sempre validare!
static ListImportaCSVSicuro(string percorso) { List studenti = new List (); int numeroRiga = 0; try { using (StreamReader reader = new StreamReader(percorso)) { string header = reader.ReadLine(); numeroRiga++; string riga; while ((riga = reader.ReadLine()) != null) { numeroRiga++; try { string[] campi = riga.Split(';'); if (campi.Length != 4) { Console.WriteLine($"⚠️ Riga {numeroRiga}: numero campi errato"); continue; } Studente s = new Studente { Nome = campi[0].Trim(), Cognome = campi[1].Trim(), Matricola = int.Parse(campi[2]), Media = double.Parse(campi[3]) }; studenti.Add(s); } catch (FormatException) { Console.WriteLine($"⚠️ Riga {numeroRiga}: formato dati non valido"); } } } } catch (FileNotFoundException) { Console.WriteLine($"❌ File '{percorso}' non trovato"); } catch (IOException ex) { Console.WriteLine($"❌ Errore I/O: {ex.Message}"); } return studenti; }
🚀 Esempio Completo: Sistema Gestione Note
Sistema completo per creare, salvare, caricare ed eliminare note su file.
using System;
public class Nota
{
public string Titolo { get; set; }
public string Contenuto { get; set; }
public DateTime DataCreazione { get; set; }
public Nota()
{
DataCreazione = DateTime.Now;
}
public Nota(string titolo, string contenuto)
{
Titolo = titolo;
Contenuto = contenuto;
DataCreazione = DateTime.Now;
}
public override string ToString()
{
return $"[{DataCreazione:dd/MM/yyyy HH:mm}] {Titolo}\n{Contenuto}";
}
}
using System;
using System.IO;
using System.Collections.Generic;
public class GestoreNote
{
private string cartellaNote;
public GestoreNote()
{
// Crea cartella Note nei Documenti
string documenti = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
cartellaNote = Path.Combine(documenti, "MieNote");
// Crea directory se non esiste
if (!Directory.Exists(cartellaNote))
{
Directory.CreateDirectory(cartellaNote);
}
}
// Salva nota su file
public void SalvaNota(Nota nota)
{
try
{
// Nome file: titolo + timestamp per unicità
string nomeFile = $"{nota.Titolo}_{nota.DataCreazione:yyyyMMdd_HHmmss}.txt";
// Rimuovi caratteri non validi dal nome file
foreach (char c in Path.GetInvalidFileNameChars())
{
nomeFile = nomeFile.Replace(c, '_');
}
string percorso = Path.Combine(cartellaNote, nomeFile);
using (StreamWriter writer = new StreamWriter(percorso))
{
writer.WriteLine($"Titolo: {nota.Titolo}");
writer.WriteLine($"Data: {nota.DataCreazione}");
writer.WriteLine("---");
writer.WriteLine(nota.Contenuto);
}
Console.WriteLine($"✅ Nota salvata: {nomeFile}");
}
catch (IOException ex)
{
Console.WriteLine($"❌ Errore salvataggio: {ex.Message}");
}
}
// Carica nota da file
public Nota CaricaNota(string nomeFile)
{
try
{
string percorso = Path.Combine(cartellaNote, nomeFile);
if (!File.Exists(percorso))
{
Console.WriteLine($"❌ File '{nomeFile}' non trovato");
return null;
}
string[] righe = File.ReadAllLines(percorso);
if (righe.Length < 4)
{
Console.WriteLine("❌ File corrotto");
return null;
}
Nota nota = new Nota();
nota.Titolo = righe[0].Replace("Titolo: ", "");
nota.DataCreazione = DateTime.Parse(righe[1].Replace("Data: ", ""));
// Contenuto = tutte le righe dopo "---"
nota.Contenuto = string.Join("\n", righe, 3, righe.Length - 3);
return nota;
}
catch (Exception ex)
{
Console.WriteLine($"❌ Errore caricamento: {ex.Message}");
return null;
}
}
// Lista tutte le note
public List ListaNote()
{
List note = new List();
try
{
string[] files = Directory.GetFiles(cartellaNote, "*.txt");
foreach (string file in files)
{
note.Add(Path.GetFileName(file));
}
}
catch (IOException ex)
{
Console.WriteLine($"❌ Errore lettura directory: {ex.Message}");
}
return note;
}
// Elimina nota
public void EliminaNota(string nomeFile)
{
try
{
string percorso = Path.Combine(cartellaNote, nomeFile);
if (File.Exists(percorso))
{
File.Delete(percorso);
Console.WriteLine($"✅ Nota eliminata: {nomeFile}");
}
else
{
Console.WriteLine($"❌ File '{nomeFile}' non trovato");
}
}
catch (IOException ex)
{
Console.WriteLine($"❌ Errore eliminazione: {ex.Message}");
}
}
// Esporta tutte le note in CSV
public void EsportaCSV(string percorsoCSV)
{
try
{
List fileNote = ListaNote();
using (StreamWriter writer = new StreamWriter(percorsoCSV))
{
writer.WriteLine("Titolo;Data;Contenuto");
foreach (string nomeFile in fileNote)
{
Nota nota = CaricaNota(nomeFile);
if (nota != null)
{
string contenutoCSV = nota.Contenuto.Replace("\n", " ");
writer.WriteLine($"{nota.Titolo};{nota.DataCreazione:dd/MM/yyyy HH:mm};{contenutoCSV}");
}
}
}
Console.WriteLine($"✅ Esportate {fileNote.Count} note in '{percorsoCSV}'");
}
catch (IOException ex)
{
Console.WriteLine($"❌ Errore esportazione: {ex.Message}");
}
}
}
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
GestoreNote gestore = new GestoreNote();
bool continua = true;
Console.WriteLine("=== GESTORE NOTE ===\n");
while (continua)
{
Console.WriteLine("\nMenu:");
Console.WriteLine("1. Crea nuova nota");
Console.WriteLine("2. Visualizza note");
Console.WriteLine("3. Leggi nota");
Console.WriteLine("4. Elimina nota");
Console.WriteLine("5. Esporta in CSV");
Console.WriteLine("6. Esci");
Console.Write("\nScelta: ");
string scelta = Console.ReadLine();
switch (scelta)
{
case "1":
CreaNota(gestore);
break;
case "2":
VisualizzaNote(gestore);
break;
case "3":
LeggiNota(gestore);
break;
case "4":
EliminaNota(gestore);
break;
case "5":
EsportaCSV(gestore);
break;
case "6":
continua = false;
Console.WriteLine("Arrivederci!");
break;
default:
Console.WriteLine("❌ Scelta non valida");
break;
}
}
}
static void CreaNota(GestoreNote gestore)
{
Console.Write("\nTitolo: ");
string titolo = Console.ReadLine();
Console.WriteLine("Contenuto (termina con riga vuota):");
string contenuto = "";
string riga;
while (!string.IsNullOrWhiteSpace(riga = Console.ReadLine()))
{
contenuto += riga + "\n";
}
Nota nota = new Nota(titolo, contenuto.TrimEnd());
gestore.SalvaNota(nota);
}
static void VisualizzaNote(GestoreNote gestore)
{
List note = gestore.ListaNote();
if (note.Count == 0)
{
Console.WriteLine("\n📭 Nessuna nota presente");
return;
}
Console.WriteLine($"\n📝 {note.Count} note trovate:");
for (int i = 0; i < note.Count; i++)
{
Console.WriteLine($"{i + 1}. {note[i]}");
}
}
static void LeggiNota(GestoreNote gestore)
{
VisualizzaNote(gestore);
Console.Write("\nNome file da leggere: ");
string nomeFile = Console.ReadLine();
Nota nota = gestore.CaricaNota(nomeFile);
if (nota != null)
{
Console.WriteLine("\n" + new string('=', 50));
Console.WriteLine(nota.ToString());
Console.WriteLine(new string('=', 50));
}
}
static void EliminaNota(GestoreNote gestore)
{
VisualizzaNote(gestore);
Console.Write("\nNome file da eliminare: ");
string nomeFile = Console.ReadLine();
Console.Write($"Sei sicuro di eliminare '{nomeFile}'? (s/n): ");
if (Console.ReadLine()?.ToLower() == "s")
{
gestore.EliminaNota(nomeFile);
}
}
static void EsportaCSV(GestoreNote gestore)
{
Console.Write("\nNome file CSV (es. note.csv): ");
string nomeFile = Console.ReadLine();
gestore.EsportaCSV(nomeFile);
}
}
- Path.Combine: Gestione percorsi cross-platform
- Directory.CreateDirectory: Creazione automatica cartella
- StreamWriter/Reader: Lettura/scrittura controllata
- using statement: Chiusura automatica file
- Try-catch: Gestione robustaeccezioni I/O
- Nome file sicuro: Rimozione caratteri non validi
- CSV export: Esportazione dati strutturati
- WriteAllText/Lines
- ReadAllText/Lines
- AppendAllText
- Exists, Copy, Delete
- StreamWriter (scrittura)
- StreamReader (lettura)
- using statement
- File grandi
- Path.Combine
- Directory operations
- Percorsi relativi
- SpecialFolders
- File di testo
- CSV (import/export)
- Gestione errori
- Validazione dati
Lezione 16 del Corso C# Completo