1. Copia dei file
Fino ad ora le nostre operazioni sul file system erano qualcosa come "colpo singolo": crei un file, lo leggi, lo cancelli — e basta. Ma nel mondo reale capita spesso di dover copiare contenuti: backup di documenti, duplicazione di template, automazione dei flussi di lavoro con i dati. Copiare sembra una cosa da bambini (Ctrl+C e Ctrl+V — il nostro pane quotidiano), ma in realtà ci sono molti dettagli da considerare.
Oggi vedremo tutti i modi per copiare file e cartelle — dai più semplici ai leggermente più complessi. Vedremo anche come le classi built-in di .NET affrontano il problema, quali sono i limiti, quali errori possono presentarsi e cosa fare se ci sono duemila directory e un milione di file.
La classe File e il metodo Copy
Il modo più semplice per copiare un file è usare il metodo statico File.Copy. Questo metodo prende il percorso del file sorgente, il percorso del nuovo file e un parametro opzionale: permettere la sovrascrittura se il file esiste già.
using System.IO;
// Esempio semplice di copia file
File.Copy("source.txt", "destination.txt");
Se il file di destinazione esiste già, il metodo lancerà un'eccezione. Per permettere esplicitamente la sovrascrittura, usa il terzo parametro:
File.Copy("source.txt", "destination.txt", overwrite: true);
Nota importante: Se il secondo parametro ("destination.txt") è il percorso di una directory esistente e non di un file, si verificherà un errore. Il metodo si aspetta un percorso esattamente verso un file!
Lavorare con i percorsi
Come sempre, non dimenticare di usare Path.Combine per non cascare nelle trappole di slash doppi o backslash:
string sourcePath = Path.Combine("Data", "input.txt");
string destPath = Path.Combine("Backup", "input_backup.txt");
File.Copy(sourcePath, destPath, overwrite: true);
Gestione degli errori
Cosa può andare storto quando si copia un file? Molto: il file può essere bloccato da un altro processo, il file sorgente può mancare, potresti non avere i permessi, il disco di destinazione può essere pieno. Usa il blocco try/catch:
try
{
File.Copy("bigdata.txt", "bigdata_backup.txt", overwrite: false);
Console.WriteLine("File copiato con successo!");
}
catch (IOException ex)
{
Console.WriteLine($"Errore di I/O: {ex.Message}");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Nessun accesso al file o alla cartella.");
}
catch (Exception ex)
{
Console.WriteLine($"Altro errore: {ex.Message}");
}
La classe FileInfo e il metodo CopyTo
Se hai già un oggetto FileInfo, puoi chiamare su di esso il metodo CopyTo:
var fi = new FileInfo("report.xlsx");
fi.CopyTo("backup_report.xlsx");
Il terzo argomento (overwrite) in CopyTo è apparso solo in .NET Core 2.0+, quindi se scrivi per una versione vecchia del framework non sorprenderti per l'errore.
Copiare un file "nel nulla" (o "non beccato — non rubato")
Presta attenzione al percorso specificato per la copia. Se la cartella di destinazione non esiste, .NET lancerà un'eccezione. Quindi prima di copiare un file è consigliabile assicurarsi che la directory di destinazione esista:
string backupDir = "Backup";
if (!Directory.Exists(backupDir))
{
Directory.CreateDirectory(backupDir);
}
string targetPath = Path.Combine(backupDir, "mydoc.txt");
File.Copy("mydoc.txt", targetPath);
2. Copia delle directory: non è roba per i deboli di cuore
Qui cominciano le vere avventure! In .NET non esiste un metodo "magico" Directory.Copy che faccia tutto con una sola riga (come per la classe File). Dovremo mettere un po' di mano e scrivere una funzione per copiare ricorsivamente tutti i file e le sottocartelle.
Perché non esiste Directory.Copy?
La copia delle directory non è sempre banale. Bisogna considerare che ogni cartella può contenere file, sottocartelle, file nascosti, file con permessi speciali e percorsi lunghi. Quindi gli sviluppatori .NET hanno pensato: "Lasciamo che gli sviluppatori decidano cosa copiare." Ma noi non cerchiamo la via facile — scriviamo la nostra funzione!
Esempio di copia ricorsiva della directory
Compito: copiare il contenuto di una cartella (e tutto il suo sotto-universo) in un'altra.
using System;
using System.IO;
void CopyDirectory(string sourceDir, string destDir, bool recursive)
{
// Verifichiamo che la cartella sorgente esista
if (!Directory.Exists(sourceDir))
throw new DirectoryNotFoundException($"Cartella sorgente non trovata: {sourceDir}");
// Creiamo la cartella di destinazione, se non esiste già
if (!Directory.Exists(destDir))
Directory.CreateDirectory(destDir);
// Copiamo tutti i file
foreach (string filePath in Directory.GetFiles(sourceDir))
{
string fileName = Path.GetFileName(filePath);
string destFilePath = Path.Combine(destDir, fileName);
File.Copy(filePath, destFilePath, overwrite: true);
}
// Se ricorsivo — copiamo tutte le sottocartelle
if (recursive)
{
foreach (string dirPath in Directory.GetDirectories(sourceDir))
{
string dirName = Path.GetFileName(dirPath);
string destSubDir = Path.Combine(destDir, dirName);
// Chiamata ricorsiva!
CopyDirectory(dirPath, destSubDir, recursive);
}
}
}
Uso:
CopyDirectory("C:\\MyData", "D:\\Backup\\MyData", recursive: true);
Questa funzione crea la struttura di directory e copia tutti i file, includendo il contenuto delle sottocartelle.
Analizziamo il codice e i dettagli
Prima creiamo la directory di destinazione (se non esiste) — altrimenti cercando di copiare un file lì otterremo un errore. Dentro ogni ciclo usiamo Path.GetFileName per non perdere il nome del file o della cartella quando compiliamo il nuovo percorso.
A proposito, se copi la directory dentro se stessa o in una sua sottocartella, otterrai una ricorsione epica… e una StackOverflowException. Non copiare "C:\\Data" in "C:\\Data\\Backup". Il computer si offende!
Copiare solo i file (senza sottocartelle)
A volte è sufficiente copiare solo i file del livello superiore (senza scendere nelle sottocartelle):
void CopyFilesOnly(string sourceDir, string destDir)
{
if (!Directory.Exists(destDir))
Directory.CreateDirectory(destDir);
foreach (string filePath in Directory.GetFiles(sourceDir))
{
string fileName = Path.GetFileName(filePath);
string destFilePath = Path.Combine(destDir, fileName);
File.Copy(filePath, destFilePath, overwrite: true);
}
}
Esempio — implementiamo il backup
Aggiungiamo questa funzionalità alla nostra "HomeApp" che sviluppiamo durante il corso. Facciamo in modo che ora sia capace di creare una copia di backup dei suoi dati.
using System;
using System.IO;
namespace HomeApp
{
class Program
{
static void CopyDirectory(string sourceDir, string destDir, bool recursive)
{
if (!Directory.Exists(sourceDir))
throw new DirectoryNotFoundException($"Cartella sorgente non trovata: {sourceDir}");
if (!Directory.Exists(destDir))
Directory.CreateDirectory(destDir);
foreach (string filePath in Directory.GetFiles(sourceDir))
{
string fileName = Path.GetFileName(filePath);
string destFilePath = Path.Combine(destDir, fileName);
File.Copy(filePath, destFilePath, overwrite: true);
}
if (recursive)
{
foreach (string dirPath in Directory.GetDirectories(sourceDir))
{
string dirName = Path.GetFileName(dirPath);
string destSubDir = Path.Combine(destDir, dirName);
CopyDirectory(dirPath, destSubDir, recursive);
}
}
}
static void Main(string[] args)
{
Console.WriteLine("Inserisci il percorso della directory di lavoro:");
string source = Console.ReadLine()!;
Console.WriteLine("Inserisci il percorso della cartella per il backup:");
string dest = Console.ReadLine()!;
try
{
CopyDirectory(source, dest, recursive: true);
Console.WriteLine("Backup creato con successo!");
}
catch (Exception ex)
{
Console.WriteLine("Errore durante la copia: " + ex.Message);
}
}
}
}
3. Dettagli utili
Confronto: file vs directory — tabella
| File | Directory | |
|---|---|---|
| Metodo built-in | |
- |
| Orientato agli oggetti | |
- |
| Gestione della profondità | Non richiesta | Richiesta ricorsione |
| Creazione della struttura | Automatico | Devi creare manualmente la gerarchia |
| Pericolo di ricorsione | No | Sì — non copiare "se stessa" |
Applicazioni pratiche e esercizi per casa
I compiti di copia di file e cartelle sono ovunque: dal backup alla migrazione dei dati o all'aggiornamento delle risorse in launcher di giochi. Spesso queste procedure si automatizzano per evitare lavoro manuale e per non ritrovarsi il lunedì mattina a dire "oh, ho dimenticato un file!".
La copia delle directory è utile in tutti gli scenari in cui bisogna preservare non solo il contenuto ma anche lo "scheletro" della struttura, incluse sottocartelle, file annidati e impostazioni.
Checklist per copiare (per non sbagliare)
- Esistenza della sorgente
- Esistenza della cartella di destinazione (crearla se serve tramite Directory.CreateDirectory)
- Strategia di sovrascrittura dei file — serve overwrite: true?
- Non stai copiando la cartella "dentro se stessa"?
- Non superi la lunghezza massima del percorso (soprattutto su Windows)?
- Consideri file nascosti/sistema?
4. Caratteristiche e errori tipici nella copia
Permessi
La copia può fallire se il programma non ha i permessi di lettura sui file sorgente o di scrittura sulla directory di destinazione. In tal caso riceverai una delle varianti di UnauthorizedAccessException. La soluzione — eseguire il programma come amministratore (solo se davvero necessario!) o scegliere cartelle più adeguate per l'operazione.
File in uso
Se un file è aperto in un altro programma (per esempio Excel lo ha bloccato), File.Copy può lanciare un'eccezione. Assicurati che l'app che blocca il file non interferisca, oppure implementa una logica di retry ( try-catch con tentativi di ripetizione).
Sovrascrittura dei file
Quale politica scegliere: sovrascrivere i file di destinazione o lasciare intatti quelli esistenti? Per i backup di solito ha senso sovrascrivere (overwrite: true), mentre per duplicare template potrebbe essere preferibile non farlo (overwrite: false).
Copia di file nascosti e di sistema
Per default i metodi che abbiamo usato (Directory.GetFiles) restituiscono tutti i file, inclusi quelli nascosti e di sistema. Se vuoi saltarli, filtra esplicitamente:
foreach (string filePath in Directory.GetFiles(sourceDir))
{
var attr = File.GetAttributes(filePath);
if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden)
continue; // Saltiamo i file nascosti
// Resto del codice di copia
}
Errori legati a percorsi troppo lunghi
Windows per molto tempo ha limitato la lunghezza dei percorsi a circa 260 caratteri. Nelle versioni moderne questo limite può essere rimosso, ma se lavori su sistemi vecchi i percorsi lunghi possono essere un problema.
Collegamenti simbolici e "junction"
In scenari specifici nelle directory possono esserci symbolic link o "junctions". I metodi normali di copia possono copiarli come normali cartelle o ignorarli. Per la maggior parte delle esercitazioni didattiche non è importante, ma se lavori con cartelle di sistema — fai attenzione.
GO TO FULL VERSION