1. Введение
Представьте себе архив фотографий за несколько лет — тысячи файлов в одной папке (классика работы пользователя Windows), а теперь вообразите задачу: скопировать только .jpg-файлы, которые созданы в этом году, в отдельную папку для обработки. Или вам нужно переименовать все отчеты с приставкой "old_", чтобы различать старую и новую версию. Даже если вы далеки от архивов фото, массовые операции нужны практически в любом проекте с обработкой данных, логов, резервных копий или автоматизации. Они встречаются и на собеседованиях!
Массовые операции — это отличный повод освоить:
- Циклы и LINQ для перебора содержимого директорий
- Практику работы с путями (Path)
- Основы фильтрации, поиска, шаблонного переименования
- Важные вопросы: безопасность, ошибки, перезаписи
Погнали — и да пребудет с вами сила побеждать файловый хаос!
2. Массовое копирование файлов
Как устроено: общий принцип
Для массовой операции обычно надо:
- Получить список нужных файлов (например, все .txt в папке)
- Для каждого файла выполнить нужное действие (копирование, удаление и т.д.)
Это можно реализовать через:
- 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 нет — это ловушка для джедаев!). Приходится копировать вручную:
- Создать целевую папку (если её нет)
- Копировать все файлы (смотрите знакомый нам пример)
- Рекурсивно копировать все подпапки (каждую обрабатываем как новую задачу копирования)
Универсальная функция: копирование папки рекурсивно
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, а при переименовании/перемещении заранее проверяйте существование файла целевого пути или применяйте стратегию конфликтов (переименование с суффиксом, пропуск, журналирование).
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ