JavaRush /Курсы /C# SELF /Массовые операции с файлами

Массовые операции с файлами

C# SELF
40 уровень , 4 лекция
Открыта

1. Введение

Представьте себе архив фотографий за несколько лет — тысячи файлов в одной папке (классика работы пользователя Windows), а теперь вообразите задачу: скопировать только .jpg-файлы, которые созданы в этом году, в отдельную папку для обработки. Или вам нужно переименовать все отчеты с приставкой "old_", чтобы различать старую и новую версию. Даже если вы далеки от архивов фото, массовые операции нужны практически в любом проекте с обработкой данных, логов, резервных копий или автоматизации. Они встречаются и на собеседованиях!

Массовые операции — это отличный повод освоить:

  • Циклы и LINQ для перебора содержимого директорий
  • Практику работы с путями (Path)
  • Основы фильтрации, поиска, шаблонного переименования
  • Важные вопросы: безопасность, ошибки, перезаписи

Погнали — и да пребудет с вами сила побеждать файловый хаос!

2. Массовое копирование файлов

Как устроено: общий принцип

Для массовой операции обычно надо:

  1. Получить список нужных файлов (например, все .txt в папке)
  2. Для каждого файла выполнить нужное действие (копирование, удаление и т.д.)

Это можно реализовать через:

  • Directory.GetFiles() — получить список файлов,
  • Цикл foreach — пройтись по каждому файлу и совершить нужную операцию.

Пример: копирование всех .txt файлов из одной папки в другую


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

// Получаем список всех .txt файлов в исходной папке
string[] txtFiles = Directory.GetFiles(sourceDir, "*.txt");

foreach (string srcPath in txtFiles)
{
    // Получаем только имя файла из полного пути
    string fileName = Path.GetFileName(srcPath);

    // Формируем полный путь для файла в целевой папке
    string destPath = Path.Combine(destDir, fileName);

    // Копируем файл
    File.Copy(srcPath, destPath, overwrite: true); // overwrite - если файл уже есть, заменить
    Console.WriteLine($"Copied: {fileName}");
}

Console.WriteLine("Все .txt файлы успешно скопированы!");

Обратите внимание: в этом примере мы используем фильтр "*.txt" — это паттерн поиска файловых имён, как в Windows.

Визуализация (схема)

+----------------+     [*.txt]     +----------------+
|   C:\Source    | ---filter-----> |   C:\Target    |
| a.txt          |   foreach +     |                |
| b.txt          | --copy--------> | a.txt (copied) |
| c.jpg          |                | b.txt (copied) |
+----------------+                +----------------+
(.jpg файлы игнорируются, копируются только .txt)

3. Массовое удаление файлов

Массовое удаление файлов — весьма популярная операция. Например, часто нужно проводить чистку временных файлов, автоматическое удаление старых логов или jpg-фото, которые вам больше не нужны.

Пример: удаляем все файлы старше 30 дней


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

DirectoryInfo di = new DirectoryInfo(dir);
// Получаем все файлы в папке
foreach (FileInfo file in di.GetFiles())
{
    // Проверяем дату последней модификации
    if (file.LastWriteTime < DateTime.Now.AddDays(-daysOld))
    {
        file.Delete();
        Console.WriteLine($"Удалён: {file.Name}");
    }
}

Фишка: FileInfo.LastWriteTime — очень удобно для условий "старше N дней".

4. Массовое переименование файлов

Иногда нужно переименовать множество файлов по шаблону. Например, добавить общий префикс, изменить расширения, или просто пронумеровать всё подряд. В .NET это делается также — получаем список файлов, затем переименовываем их с помощью File.Move().

Пример: добавим префикс "old_" ко всем .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);

    // Переименовываем (по факту, это перемещение "в ту же папку с новым именем")
    File.Move(oldPath, newPath);
    Console.WriteLine($"Переименовано: {fileName} -> old_{fileName}");
}

Важный момент: если в папке уже есть файл с новым именем, возникнет исключение. Можно обрабатывать его через try-catch, если нужно.

5. Копирование целых директорий со всем содержимым

Для простых операций с одной папкой нет средства "скопировать всю папку" одной строкой (Directory.Copy нет — это ловушка для джедаев!). Приходится копировать вручную:

  1. Создать целевую папку (если её нет)
  2. Копировать все файлы (смотрите знакомый нам пример)
  3. Рекурсивно копировать все подпапки (каждую обрабатываем как новую задачу копирования)

Универсальная функция: копирование папки рекурсивно


using System;
using System.IO;

class Program
{
    static void CopyDirectory(string sourceDir, string destDir, bool overwrite = true)
    {
        // Создаем папку, если её нет
        Directory.CreateDirectory(destDir);

        // Копируем все файлы
        foreach (string filePath in Directory.GetFiles(sourceDir))
        {
            string fileName = Path.GetFileName(filePath);
            string destFile = Path.Combine(destDir, fileName);
            File.Copy(filePath, destFile, overwrite);
        }

        // Копируем все подпапки (рекурсивно)
        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("Директория успешно скопирована!");
    }
}

Блок-схема

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

6. Массовая фильтрация, поиск и обработка файлов

Допустим, вы хотите не просто перебрать папку, а выбрать по нескольким критериям: например, только картинки, размер которых превышает 5 МБ, и они были созданы в 2024 году! Для этого удобно совмещать LINQ с классами файловой системы.

Пример: выводим имена больших и свежих картинок


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

В этом примере мы используем LINQ для "цепочек" фильтрации — и пишем реалистичный код, который встречается на работе.

7. Проход по вложенным папкам: рекурсия и перебор

Часто требуется не только одну директорию обработать, но и все вложенные (например, удалить все временные файлы по всей структуре архива). У методов получения файлов (Directory.GetFiles и DirectoryInfo.GetFiles) есть вариант со специальным параметром SearchOption.AllDirectories, который делает это за вас!

Пример: найти и удалить все файлы .tmp во всех подпапках


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

foreach (string file in tmpFiles)
{
    File.Delete(file);
    Console.WriteLine($"Удалён: {file}");
}
Console.WriteLine("Все временные файлы удалены.");

Внимание: Будьте осторожны с этим флагом — он может найти файлы даже в очень глубоких вложениях!

8. Массовое создание файлов и директорий

Иногда задача обратная — автоматически создать нужную структуру каталогов или сгенерировать много файлов.

Пример: создание 10 папок и 10 файлов в каждой


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, $"Это файл номер {j} в папке {i}");
    }
}

Console.WriteLine("Папки и файлы созданы!");

Фишка: Можно быстро собирать тестовую инфраструктуру, генерировать "рыбу" для тестов, обучения и пр.

9. Массовое перемещение файлов

Аналогично копированию, только используем File.Move вместо File.Copy. Хорошо подходит для сортировки файлов по папкам.

Пример: сортируем файлы по расширениям

Представим ситуацию: в папке куча файлов разных типов, и вы хотите рассортировать их по папкам .jpg, .pdf, .docx и т.п.


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); // не страшно, если уже есть

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

    if (!File.Exists(destPath))
    {
        File.Move(path, destPath);
        Console.WriteLine($"Перемещено: {fileName} -> {destDir}");
    }
    else
    {
        Console.WriteLine($"Файл уже существует в {destDir}, пропускаем: {fileName}");
    }
}

Файлы разъедутся "по коробкам", а вы почувствуете себя digital-мари Кондо.

10. Типичные ошибки и нюансы массовых операций

Когда работаешь не с одним файлом, а с сотнями, вылезают интересные детали.

Например, некоторые файлы могут быть открыты другой программой — попытка удалить или скопировать их приведёт к ошибке. Также нередки ситуации, когда в целевой папке уже есть файл с нужным именем. Если конструкция не предполагает перезапись, код упадёт с исключением. Иногда у файлов или папок может не быть доступа по правам, или вы случайно удалите что-то важное рекурсивно.

Классическая ошибка — отрабатывать операции без обработки ошибок: если где-то в середине случится сбой (например, отсутствие прав на один из файлов), цикл оборвётся и все последующие действия не будут выполнены. Поэтому для надёжных массовых операций почти всегда применяют обработку исключений через try-catch внутри цикла, чтобы проблемный файл можно было пропустить, а остальные всё же обработать.

И не забывайте про параметры перезаписи: где это уместно, используйте overwrite: true в File.Copy, а при переименовании/перемещении заранее проверяйте существование файла целевого пути или применяйте стратегию конфликтов (переименование с суффиксом, пропуск, журналирование).

2
Задача
C# SELF, 40 уровень, 4 лекция
Недоступна
Сортировка файлов по папкам в зависимости от их расширения
Сортировка файлов по папкам в зависимости от их расширения
1
Опрос
Создание и удаление файлов, 40 уровень, 4 лекция
Недоступен
Создание и удаление файлов
Управление файлами и директориями
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ