CodeGym /Cursos /C# SELF /Operaciones masivas con archivos

Operaciones masivas con archivos

C# SELF
Nivel 40 , Lección 4
Disponible

1. Introducción

Imagínate un archivo de fotos acumulado durante varios años — miles de archivos en una carpeta (clásico en Windows), y ahora la tarea: copiar solo los archivos .jpg creados este año a una carpeta separada para procesarlos. O necesitas renombrar todos los informes con el prefijo "old_" para distinguir la versión antigua de la nueva. Incluso si estás lejos de archivos de fotos, las operaciones masivas son necesarias en casi cualquier proyecto que trate datos, logs, backups o automatización. ¡También salen en entrevistas!

Las operaciones masivas son una excelente excusa para aprender:

  • Bucle y LINQ para recorrer el contenido de directorios
  • Práctica con rutas (Path)
  • Fundamentos de filtrado, búsqueda y renombrado por patrón
  • Cuestiones importantes: seguridad, errores, sobrescrituras

¡Vamos allá — y que la fuerza para vencer el caos de archivos te acompañe!

2. Copia masiva de archivos

Cómo funciona: principio general

Para una operación masiva normalmente hay que:

  1. Obtener la lista de archivos necesarios (por ejemplo, todos los .txt en una carpeta)
  2. Para cada archivo ejecutar la acción necesaria (copiar, eliminar, etc.)

Esto se puede implementar con:

  • Directory.GetFiles() — obtener la lista de archivos,
  • Bucle foreach — iterar por cada archivo y realizar la operación necesaria.

Ejemplo: copiar todos los archivos .txt de una carpeta a otra


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

// Obtenemos la lista de todos los archivos .txt en la carpeta de origen
string[] txtFiles = Directory.GetFiles(sourceDir, "*.txt");

foreach (string srcPath in txtFiles)
{
    // Obtenemos solo el nombre del archivo desde la ruta completa
    string fileName = Path.GetFileName(srcPath);

    // Formamos la ruta completa para el archivo en la carpeta destino
    string destPath = Path.Combine(destDir, fileName);

    // Copiamos el archivo
    File.Copy(srcPath, destPath, overwrite: true); // overwrite - si el archivo ya existe, reemplazar
    Console.WriteLine($"Copied: {fileName}");
}

Console.WriteLine("¡Todos los archivos .txt se copiaron con éxito!");

Atención: en este ejemplo usamos el filtro "*.txt" — es un patrón de búsqueda de nombres de archivo, como en Windows.

Visualización (esquema)

+----------------+     [*.txt]     +----------------+
|   C:\Source    | ---filter-----> |   C:\Target    |
| a.txt          |   foreach +     |                |
| b.txt          | --copy--------> | a.txt (copied) |
| c.jpg          |                | b.txt (copied) |
+----------------+                +----------------+
(los archivos .jpg se ignoran, se copian solo los .txt)

3. Eliminación masiva de archivos

La eliminación masiva de archivos es una operación muy común. Por ejemplo, a menudo hace falta limpiar archivos temporales, eliminar automáticamente logs antiguos o fotos jpg que ya no necesitas.

Ejemplo: eliminamos todos los archivos con más de 30 días


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

DirectoryInfo di = new DirectoryInfo(dir);
// Obtenemos todos los archivos en la carpeta
foreach (FileInfo file in di.GetFiles())
{
    // Comprobamos la fecha de la última modificación
    if (file.LastWriteTime < DateTime.Now.AddDays(-daysOld))
    {
        file.Delete();
        Console.WriteLine($"Eliminado: {file.Name}");
    }
}

Tip: FileInfo.LastWriteTime — muy útil para condiciones "más antiguo que N días".

4. Renombrado masivo de archivos

A veces hace falta renombrar muchos archivos según un patrón. Por ejemplo, añadir un prefijo común, cambiar extensiones, o numerarlos secuencialmente. En .NET se hace igual: obtenemos la lista de archivos y luego los renombramos con File.Move().

Ejemplo: añadimos el prefijo "old_" a todos los archivos .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);

    // Renombramos (en realidad, es mover "a la misma carpeta con un nombre nuevo")
    File.Move(oldPath, newPath);
    Console.WriteLine($"Renombrado: {fileName} -> old_{fileName}");
}

Punto importante: si en la carpeta ya existe un archivo con el nuevo nombre, se lanzará una excepción. Puedes manejarla con try-catch si hace falta.

5. Copiar directorios enteros con todo su contenido

Para operaciones simples con una carpeta no hay un método "copiar toda la carpeta" en una sola línea (Directory.Copy no existe — ¡trampa de jedis!). Hay que copiar manualmente:

  1. Crear la carpeta destino (si no existe)
  2. Copiar todos los archivos (ver ejemplo conocido)
  3. Copiar recursivamente todas las subcarpetas (cada una se procesa como una nueva tarea de copia)

Función universal: copiar carpeta recursivamente


using System;
using System.IO;

class Program
{
    static void CopyDirectory(string sourceDir, string destDir, bool overwrite = true)
    {
        // Creamos la carpeta si no existe
        Directory.CreateDirectory(destDir);

        // Copiamos todos los archivos
        foreach (string filePath in Directory.GetFiles(sourceDir))
        {
            string fileName = Path.GetFileName(filePath);
            string destFile = Path.Combine(destDir, fileName);
            File.Copy(filePath, destFile, overwrite);
        }

        // Copiamos todas las subcarpetas (recursivamente)
        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("¡Directorio copiado con éxito!");
    }
}

Diagrama de flujo

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

6. Filtrado masivo, búsqueda y procesamiento de archivos

Supongamos que quieres no solo recorrer una carpeta, sino seleccionar según varios criterios: por ejemplo, solo imágenes cuyo tamaño exceda 5 MB y que fueron creadas en 2024. Para eso es cómodo combinar LINQ con las clases del sistema de archivos.

Ejemplo: mostrar nombres de imágenes grandes y recientes


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} MB)");
}

En este ejemplo usamos LINQ para cadenas de filtros — y escribimos código realista que aparece en el trabajo.

7. Recorrer carpetas anidadas: recursión y enumeración

A menudo necesitas procesar no solo un directorio, sino todos los anidados (por ejemplo, eliminar todos los archivos temporales en toda la estructura). Los métodos para obtener archivos (Directory.GetFiles y DirectoryInfo.GetFiles) tienen una variante con el parámetro especial SearchOption.AllDirectories, que lo hace por ti.

Ejemplo: encontrar y eliminar todos los archivos .tmp en todas las subcarpetas


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

foreach (string file in tmpFiles)
{
    File.Delete(file);
    Console.WriteLine($"Eliminado: {file}");
}
Console.WriteLine("Todos los archivos temporales han sido eliminados.");

Atención: ten cuidado con este flag — puede encontrar archivos incluso en anidamientos muy profundos.

8. Creación masiva de archivos y directorios

A veces la tarea es la inversa — crear automáticamente la estructura de carpetas necesaria o generar muchos archivos.

Ejemplo: crear 10 carpetas y 10 archivos en cada una


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, $"Este es el archivo número {j} en la carpeta {i}");
    }
}

Console.WriteLine("¡Carpetas y archivos creados!");

Tip: puedes montar rápidamente infraestructura de prueba, generar "mock" para tests, para aprendizaje, etc.

9. Movimiento masivo de archivos

Similar a la copia, pero usamos File.Move en lugar de File.Copy. Ideal para clasificar archivos en carpetas.

Ejemplo: clasificamos archivos por extensiones

Imagina: en una carpeta hay montones de archivos de muchos tipos, y quieres ordenarlos en carpetas .jpg, .pdf, .docx, etc.


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); // no pasa nada si ya existe

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

    if (!File.Exists(destPath))
    {
        File.Move(path, destPath);
        Console.WriteLine($"Movido: {fileName} -> {destDir}");
    }
    else
    {
        Console.WriteLine($"El archivo ya existe en {destDir}, se omite: {fileName}");
    }
}

Los archivos se repartirán "en cajas", y te sentirás como la Marie Kondo digital.

10. Errores típicos y matices de las operaciones masivas

Cuando trabajas no con un archivo sino con cientos, salen detalles interesantes.

Por ejemplo, algunos archivos pueden estar abiertos por otra aplicación — intentar borrarlos o copiarlos dará error. También es común que en la carpeta destino ya exista un archivo con el nombre deseado. Si la lógica no prevé sobrescritura, el código lanzará una excepción. A veces los archivos o carpetas no son accesibles por permisos, o puedes borrar algo importante de forma recursiva por accidente.

El error clásico es ejecutar operaciones sin manejo de errores: si en algún punto ocurre un fallo (por ejemplo, falta de permisos en uno de los archivos), el bucle se interrumpirá y las acciones restantes no se ejecutarán. Por eso, para operaciones masivas fiables casi siempre se usa manejo de excepciones con try-catch dentro del bucle, para poder saltar el archivo problemático y seguir procesando el resto.

Y no olvides los parámetros de sobrescritura: donde tenga sentido, usa overwrite: true en File.Copy, y al renombrar/mover comprueba previamente si existe el archivo destino o aplica una estrategia de conflictos (renombrar con sufijo, omitir, registrar en log).

1
Cuestionario/control
Creación y eliminación de archivos, nivel 40, lección 4
No disponible
Creación y eliminación de archivos
Gestión de archivos y directorios
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION