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.

✨ Cosa Puoi Fare con File I/O

Configurazioni

Salvare impostazioni app

Dati Utente

Esportare/importare informazioni

Log

Tracciare eventi e errori

Report

Generare file CSV/JSON

📁 Namespace Necessario

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.

1
Scrivere File di Testo

Metodi per scrivere testo su file in un colpo solo.

File.WriteAllText – Scrittura Semplice
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!");
    }
}

File.WriteAllLines – Scrittura Array
// 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

2
Leggere File di Testo

Metodi per leggere contenuto file.

File.ReadAllText – Lettura Completa
// 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.

File.ReadAllLines – Lettura Righe
// 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

3
Aggiungere Testo a File Esistente

Append invece di sovrascrivere.

File.AppendAllText – Aggiunta
// 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

4
Operazioni su File

Verificare esistenza, copiare, eliminare, spostare.

Operazioni File Comuni
// 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}");

📌 Metodi Principali di File

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

File (metodi statici)

  • Operazioni semplici e veloci
  • Tutto in memoria contemporaneamente
  • Ottimo per file piccoli
StreamReader/Writer

  • Controllo granulare
  • Lettura/scrittura progressiva
  • Ottimo per file grandi

1
StreamWriter – Scrittura

Scrive su file riga per riga.

StreamWriter Base
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

StreamWriter con Append
// 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

2
StreamReader – Lettura

Legge file riga per riga o carattere per carattere.

StreamReader – Lettura Riga per Riga
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++;
    }
}

StreamReader – Lettura Completa
using (StreamReader reader = new StreamReader("documento.txt")) 
{
    // Legge tutto il contenuto in una volta
    string tuttoIlContenuto = reader.ReadToEnd();
    Console.WriteLine(tuttoIlContenuto);
}

⚠️ IMPORTANTE: using Statement

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.

Path – Operazioni su Percorsi
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"

Directory – Operazioni su Cartelle
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);

🔧 Best Practice: Percorsi Relativi

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).

Scrittura CSV – Esporta Dati
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

Lettura CSV – Importa Dati
static List ImportaCSV(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}");
}

⚠️ Gestione Errori CSV

I file CSV possono avere valori mancanti o formati errati. Sempre validare!

static List ImportaCSVSicuro(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.

Nota.cs – Classe Nota
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}";
    }
}

GestoreNote.cs – Logica File I/O
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}");
        }
    }
}

Program.cs – Applicazione Completa
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);
    }
}

💡 Analisi Sistema File I/O
  • 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

🎯 Riepilogo Lezione

Classe File:

  • WriteAllText/Lines
  • ReadAllText/Lines
  • AppendAllText
  • Exists, Copy, Delete
Stream:

  • StreamWriter (scrittura)
  • StreamReader (lettura)
  • using statement
  • File grandi
Percorsi:

  • Path.Combine
  • Directory operations
  • Percorsi relativi
  • SpecialFolders
Formati:

  • File di testo
  • CSV (import/export)
  • Gestione errori
  • Validazione dati

Lezione 16 del Corso C# Completo

Torna in alto