CodeGym /Corsi /C# SELF /Operazioni di massa sui file

Operazioni di massa sui file

C# SELF
Livello 40 , Lezione 4
Disponibile

1. Introduzione

Immagina un archivio di foto accumulato in anni — migliaia di file in una cartella (classico scenario Windows). Ora immagina il compito: copiare solo i file .jpg creati quest'anno in una cartella separata per l'elaborazione. Oppure devi rinominare tutti i report aggiungendo il prefisso "old_" per distinguere versione vecchia e nuova. Anche se non lavori con archivi foto, le operazioni di massa servono praticamente in tutti i progetti che trattano dati, log, backup o automazione. Sono richieste anche ai colloqui tecnici!

Le operazioni di massa sono un'ottima occasione per imparare:

  • Cicli e LINQ per attraversare il contenuto delle directory
  • Pratica con i percorsi (Path)
  • Fondamenti di filtraggio, ricerca, rinomina con pattern
  • Questioni importanti: sicurezza, errori, overwrite

Andiamo — e che la forza sia con te per sconfiggere il caos dei file!

2. Copia di massa dei file

Come funziona: principio generale

Per un'operazione di massa di solito bisogna:

  1. Ottenere la lista dei file desiderati (per esempio tutti i .txt in una cartella)
  2. Per ogni file eseguire l'azione necessaria (copia, eliminazione, ecc.)

Questo si può realizzare con:

  • Directory.GetFiles() — per ottenere la lista di file,
  • Il ciclo foreach — per iterare ogni file ed eseguire l'operazione.

Esempio: copiare tutti i file .txt da una cartella a un'altra


string sourceDir = @"C:\Source";
string destDir = @"C:\Target";

// Otteniamo la lista di tutti i file .txt nella cartella sorgente
string[] txtFiles = Directory.GetFiles(sourceDir, "*.txt");

foreach (string srcPath in txtFiles)
{
    // Otteniamo solo il nome del file dal percorso completo
    string fileName = Path.GetFileName(srcPath);

    // Formiamo il percorso completo per il file nella cartella di destinazione
    string destPath = Path.Combine(destDir, fileName);

    // Copiamo il file
    File.Copy(srcPath, destPath, overwrite: true); // overwrite - se il file esiste, sostituisci
    Console.WriteLine($"Copied: {fileName}");
}

Console.WriteLine("Tutti i file .txt sono stati copiati con successo!");

Nota: in questo esempio usiamo il filtro "*.txt" — è un pattern di ricerca dei nomi file, come in Windows.

Visualizzazione (schema)

+----------------+     [*.txt]     +----------------+
|   C:\Source    | ---filter-----> |   C:\Target    |
| a.txt          |   foreach +     |                |
| b.txt          | --copy--------> | a.txt (copied) |
| c.jpg          |                | b.txt (copied) |
+----------------+                +----------------+
(i file .jpg sono ignorati, vengono copiati solo i .txt)

3. Eliminazione di massa dei file

L'eliminazione di massa è un'operazione molto comune. Per esempio, spesso bisogna pulire file temporanei, rimuovere vecchi log o foto jpg che non servono più.

Esempio: eliminiamo tutti i file più vecchi di 30 giorni


string dir = @"C:\MyLogs";
int daysOld = 30;

DirectoryInfo di = new DirectoryInfo(dir);
// Otteniamo tutti i file nella cartella
foreach (FileInfo file in di.GetFiles())
{
    // Controlliamo la data di ultima modifica
    if (file.LastWriteTime < DateTime.Now.AddDays(-daysOld))
    {
        file.Delete();
        Console.WriteLine($"Eliminato: {file.Name}");
    }
}

Trucchetto: FileInfo.LastWriteTime è molto comodo per condizioni "più vecchio di N giorni".

4. Rinomina di massa dei file

A volte bisogna rinominare molti file seguendo uno schema. Per esempio aggiungere un prefisso comune, cambiare estensioni, o numerare tutto. In .NET si procede allo stesso modo — otteniamo la lista di file e poi li rinominiamo con File.Move().

Esempio: aggiungiamo il prefisso "old_" a tutti i file .docx


string dir = @"C:\Reports";

string[] docxFiles = Directory.GetFiles(dir, "*.docx");
foreach (string oldPath in docxFiles)
{
    string dirPath = Path.GetDirectoryName(oldPath)!;
    string fileName = Path.GetFileName(oldPath);
    string newPath = Path.Combine(dirPath, "old_" + fileName);

    // Rinominiamo (di fatto è uno spostamento "nella stessa cartella con nuovo nome")
    File.Move(oldPath, newPath);
    Console.WriteLine($"Rinominato: {fileName} -> old_{fileName}");
}

Punto importante: se nella cartella esiste già un file con il nuovo nome, verrà sollevata un'eccezione. Puoi gestirla con try-catch se necessario.

5. Copia di intere directory con tutto il contenuto

Per operazioni semplici su una cartella non esiste un metodo "copia tutta la cartella" in una riga (Directory.Copy non esiste — attenzione al tranello!). Bisogna copiare manualmente:

  1. Creare la cartella di destinazione (se non esiste)
  2. Copiare tutti i file (vedi esempio già noto)
  3. Copiare ricorsivamente tutte le sottocartelle (trattando ognuna come nuova operazione di copia)

Funzione universale: copia ricorsiva di una cartella


using System;
using System.IO;

class Program
{
    static void CopyDirectory(string sourceDir, string destDir, bool overwrite = true)
    {
        // Creiamo la cartella se non esiste
        Directory.CreateDirectory(destDir);

        // Copiamo tutti i file
        foreach (string filePath in Directory.GetFiles(sourceDir))
        {
            string fileName = Path.GetFileName(filePath);
            string destFile = Path.Combine(destDir, fileName);
            File.Copy(filePath, destFile, overwrite);
        }

        // Copiamo tutte le sottocartelle (ricorsivamente)
        foreach (string subDir in Directory.GetDirectories(sourceDir))
        {
            string dirName = Path.GetFileName(subDir);
            string destSubDir = Path.Combine(destDir, dirName);
            CopyDirectory(subDir, destSubDir, overwrite);
        }
    }

    static void Main()
    {
        string source = @"C:\Archive2023";
        string target = @"D:\Backup2023";
        CopyDirectory(source, target);
        Console.WriteLine("Directory copiata con successo!");
    }
}

Diagramma a blocchi

            CopyDirectory(A, B)
               /          \
      copy files        foreach subDir -> CopyDirectory(subDir, destSubDir)

6. Filtraggio di massa, ricerca e processing dei file

Supponiamo che tu voglia non solo scorrere una cartella, ma selezionare secondo più criteri: per esempio solo immagini il cui size supera 5 MB e sono state create nel 2024! È comodo combinare LINQ con le classi del filesystem.

Esempio: stampiamo i nomi delle immagini grandi e recenti


string dir = @"C:\Pictures";

var filtered = new DirectoryInfo(dir)
    .GetFiles("*.jpg")
    .Where(f => f.Length > 5_000_000 && f.CreationTime.Year == 2024);

foreach (var file in filtered)
{
    Console.WriteLine($"{file.Name} ({file.Length / 1024 / 1024} МБ)");
}

In questo esempio usiamo LINQ per concatenare i filtri — è codice realistico che si incontra nel lavoro.

7. Attraversamento delle sottocartelle: ricorsione e iterazione

Spesso non serve solo lavorare su una directory, ma su tutte le sue sottodirectory (per esempio eliminare tutti i file temporanei in tutta la struttura). I metodi per ottenere i file (Directory.GetFiles e DirectoryInfo.GetFiles) hanno un overload con il parametro speciale SearchOption.AllDirectories, che lo fa per te!

Esempio: trovare ed eliminare tutti i file .tmp in tutte le sottocartelle


string root = @"D:\BigFolder";
string[] tmpFiles = Directory.GetFiles(root, "*.tmp", SearchOption.AllDirectories);

foreach (string file in tmpFiles)
{
    File.Delete(file);
    Console.WriteLine($"Eliminato: {file}");
}
Console.WriteLine("Tutti i file temporanei sono stati eliminati.");

Attenzione: fai attenzione con questo flag — può trovare file anche in profondità estremamente elevate!

8. Creazione di massa di file e directory

A volte il compito è inverso — creare automaticamente la struttura di cartelle necessaria o generare molti file.

Esempio: creare 10 cartelle e 10 file in ciascuna


string root = @"C:\GeneratedFolders";

for (int i = 1; i <= 10; i++)
{
    string subDir = Path.Combine(root, $"Folder_{i}");
    Directory.CreateDirectory(subDir);

    for (int j = 1; j <= 10; j++)
    {
        string filePath = Path.Combine(subDir, $"File_{j}.txt");
        File.WriteAllText(filePath, $"Questo è il file numero {j} nella cartella {i}");
    }
}

Console.WriteLine("Cartelle e file creati!");

Trucchetto: puoi rapidamente costruire infrastrutture di test, generare "dummy" per i test, l'apprendimento ecc.

9. Spostamento di massa dei file

Simile alla copia, ma usiamo File.Move al posto di File.Copy. Ideale per organizzare file in cartelle.

Esempio: ordiniamo i file per estensione

Immagina: in una cartella ci sono tanti file di tipi diversi e vuoi smistarli in cartelle per .jpg, .pdf, .docx ecc.


string source = @"C:\Downloads";
string[] files = Directory.GetFiles(source);

foreach (string path in files)
{
    string ext = Path.GetExtension(path).TrimStart('.').ToUpper(); // "JPG", "PDF", "DOCX"
    if (string.IsNullOrEmpty(ext)) ext = "OTHER";
    string destDir = Path.Combine(source, ext);

    Directory.CreateDirectory(destDir); // non fa nulla se esiste già

    string fileName = Path.GetFileName(path);
    string destPath = Path.Combine(destDir, fileName);

    if (!File.Exists(destPath))
    {
        File.Move(path, destPath);
        Console.WriteLine($"Spostato: {fileName} -> {destDir}");
    }
    else
    {
        Console.WriteLine($"Il file esiste già in {destDir}, salto: {fileName}");
    }
}

I file verranno distribuiti "per scatole" e ti sentirai il Marie Kondo digitale.

10. Errori tipici e dettagli delle operazioni di massa

Quando lavori non con un singolo file ma con centinaia emergono dettagli interessanti.

Per esempio alcuni file possono essere aperti da un altro programma — tentare di eliminarli o copiarli causerà un errore. Spesso nella cartella di destinazione esiste già un file con il nome desiderato. Se il codice non prevede l'overwrite, fallirà con un'eccezione. A volte i file o le cartelle non sono accessibili per permessi, o potresti cancellare accidentalmente qualcosa di importante ricorsivamente.

L'errore classico è eseguire operazioni senza gestione degli errori: se qualcosa fallisce a metà (per esempio mancano i permessi su un file), il ciclo si interrompe e le azioni successive non vengono eseguite. Perciò per operazioni di massa robuste si usa quasi sempre la gestione delle eccezioni con try-catch dentro il ciclo, così il file problematico può essere saltato e gli altri processati.

E non dimenticare i parametri di overwrite: dove appropriato usa overwrite: true in File.Copy, e per rinome/spostamento verifica prima l'esistenza del file di destinazione o applica una strategia per i conflitti (rinomina con suffisso, salto, logging).

1
Sondaggio/quiz
Creazione e cancellazione dei file, livello 40, lezione 4
Non disponibile
Creazione e cancellazione dei file
Gestione di file e cartelle
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION