JavaRush /Курсы /C# SELF /Копирование файлов и директорий

Копирование файлов и директорий

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

1. Копирование файлов

До этого момента наши операции над файловой системой были чем-то вроде «одиночного удара»: создал файл, прочитал, удалил — и всё. Но в реальном мире часто встречается задача скопировать содержимое: резервное копирование документов, дублирование шаблонов, автоматизация процесса работы с данными. Копирование кажется детской задачей (Ctrl+C и Ctrl+V — наше всё), но на самом деле тут хватает нюансов.

Сегодня мы разберём все способы копирования файлов и папок — от самых простых до чуть более сложных. Мы также увидим, как встроенные классы .NET решают эту задачу, какие есть ограничения, какие ошибки могут подстерегать вас и что делать, если директорий вдруг оказалось две тысячи, а файлов — миллион.

Класс File и метод Copy

Самый простой способ копировать файл — воспользоваться статическим методом File.Copy. Этот метод принимает путь к исходному файлу, путь к новому файлу и опциональный параметр: разрешить ли перезапись, если файл уже существует.

using System.IO;

// Простой пример копирования файла
File.Copy("source.txt", "destination.txt");

Если файл назначения уже есть, метод выбросит исключение. Чтобы явно разрешить перезапись, используйте третий параметр:

File.Copy("source.txt", "destination.txt", overwrite: true);

Важный момент: Если второй параметр ("destination.txt") — путь к существующему каталогу, а не к файлу, то возникнет ошибка. Метод ожидает путь именно к файлу!

Работа с путями

Как и раньше, не забывайте использовать Path.Combine, чтобы не попасться на ловушках двойных или обратных слэшей:

string sourcePath = Path.Combine("Data", "input.txt");
string destPath = Path.Combine("Backup", "input_backup.txt");

File.Copy(sourcePath, destPath, overwrite: true);

Обработка ошибок

Что может пойти не так при копировании файла? Многое: файл может быть занят другим процессом, файл-источник может отсутствовать, у вас может не быть прав, диск назначения — переполнен. Используйте обработку исключений:

try
{
    File.Copy("bigdata.txt", "bigdata_backup.txt", overwrite: false);
    Console.WriteLine("Файл скопирован успешно!");
}
catch (IOException ex)
{
    Console.WriteLine($"Ошибка ввода-вывода: {ex.Message}");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("Нет доступа к файлу или папке.");
}
catch (Exception ex)
{
    Console.WriteLine($"Другая ошибка: {ex.Message}");
}

Класс FileInfo и метод CopyTo

Если у вас уже есть объект FileInfo, вы можете вызвать на нём метод CopyTo:

var fi = new FileInfo("report.xlsx");
fi.CopyTo("backup_report.xlsx");

Третий аргумент (overwrite) в CopyTo появился только в .NET Core 2.0+, так что если вдруг пишете под старую версию фреймворка — не удивляйтесь ошибке.

Копирование файла «в никуда» (или «не пойман — не вор»)

Внимательно относитесь к указанному пути для копирования. Если папки назначения не существует, .NET выдаст ошибку. Поэтому перед копированием файла желательно убедиться, что директория-приёмник существует:

string backupDir = "Backup";
if (!Directory.Exists(backupDir))
{
    Directory.CreateDirectory(backupDir);
}

string targetPath = Path.Combine(backupDir, "mydoc.txt");
File.Copy("mydoc.txt", targetPath);

2. Копирование директорий: задача не для слабонервных

Вот тут-то и начинаются настоящие приключения! В .NET нет встроенного «магического» метода Directory.Copy, который всё сделает за нас одной строкой (как у класса File). Придётся немного поработать руками и написать функцию для рекурсивного копирования всех файлов и подпапок.

Почему не существует Directory.Copy?

Копирование директорий не всегда тривиально. Нужно учесть, что каждая папка может содержать файлы, подпапки, скрытые файлы, файлы с особыми правами и длинными путями. Поэтому разработчики .NET решили: «Пусть программисты сами решают, что им копировать.» Но мы с вами не ищем лёгких путей — напишем свою функцию!

Пример рекурсивного копирования директории

Задача: скопировать содержимое одной папки (и всё её подмироздание) в другую.

using System;
using System.IO;

void CopyDirectory(string sourceDir, string destDir, bool recursive)
{
    // Проверяем, существует ли исходная папка
    if (!Directory.Exists(sourceDir))
        throw new DirectoryNotFoundException($"Исходная папка не найдена: {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);
        }
    }
}

Использование:

CopyDirectory("C:\\MyData", "D:\\Backup\\MyData", recursive: true);

Эта функция создаёт структуру директорий и копирует все файлы, включая содержимое подпапок.

Разбираем код и нюансы

Мы сначала создаём директорию назначения (если её нет) — иначе при попытке скопировать туда файл получим ошибку. Внутри каждого цикла используем Path.GetFileName, чтобы не потерять имя файла или папки при составлении нового пути.

Кстати, если скопировать директорию саму в себя или в свою подпапку, получите эпичную рекурсию… и StackOverflowException. Не копируйте "C:\\Data" в "C:\\Data\\Backup". Компьютер обидится!

Копирование только файлов (без подпапок)

Иногда достаточно скопировать только файлы верхнего уровня (без погружения в подпапки):

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

Пример — реализуем резервное копирование

Добавим этот функционал в наше «Домашнее приложение», которое мы развиваем по всему курсу. Пусть оно теперь умеет делать резервную копию своих данных.

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($"Исходная папка не найдена: {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("Введите путь к рабочей директории:");
            string source = Console.ReadLine()!;
            Console.WriteLine("Введите путь к папке для резервной копии:");
            string dest = Console.ReadLine()!;

            try
            {
                CopyDirectory(source, dest, recursive: true);
                Console.WriteLine("Резервная копия успешно создана!");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Ошибка при копировании: " + ex.Message);
            }
        }
    }
}

3. Полезные нюансы

Сравнение: файл vs директория — таблица

Файл Директория
Встроенный метод
File.Copy
-
Объектно-ориентированный
FileInfo.CopyTo
-
Обход вложенности Не требуется Требуется рекурсия
Создание структуры Автоматически Нужно вручную создавать вложенность
Опасность рекурсии Нет Есть — не копировать «само в себя»

Практическое применение и задачи для самостоятельной работы

Задачи копирования файлов и папок повсеместны: от резервного копирования до миграции данных или обновления ресурсов в игровых лаунчерах. Чаще всего подобные процедуры автоматизируют, чтобы не прибегать к ручной работе и не делать «ой, забыл файл!» в понедельник утром.

Копирование директорий востребовано во всех сценариях, где требуется сохранить не только содержимое, но и «каркас» структуры, включая подпапки, вложенные файлы, настройки.

Чеклист для копирования (чтобы не попасть впросак)

  • Существование источника
  • Существование папки назначения (создаём при необходимости через Directory.CreateDirectory)
  • Сценарий перезаписи файлов — нужен ли overwrite: true?
  • Не копируете ли вы папку «саму в себя»?
  • Не превышаете ли длину пути (особенно на Windows)?
  • Учитываете ли скрытые/системные файлы?

4. Особенности и типичные ошибки копирования

Права доступа

Копирование может завершиться неудачей, если у вашей программы нет прав на чтение файлов-источников или запись в директорию назначения. В таком случае вы получите одну из разновидностей UnauthorizedAccessException. Решение — запускать программу от имени администратора (только если это действительно нужно!) или выбрать нормальные папки для копирования.

Занятость файлов

Если файл открыт в другой программе (например, Excel держит его на замке), File.Copy может выбросить исключение. Убедитесь, что приложение, блокирующее файл, не мешает процессу, либо реализуйте логику повторных попыток (try-catch с ретраями).

Перезапись файлов

Какую логику выбрать: переписывать файлы назначения или не трогать их, если они есть? Для резервного копирования обычно уместна перезапись (overwrite: true), а для дублирования шаблонов — нет (overwrite: false).

Копирование скрытых и системных файлов

По умолчанию методы, которые мы использовали (Directory.GetFiles), возвращают все файлы, в том числе скрытые и системные. Если нужно пропустить их, фильтруйте явно:

foreach (string filePath in Directory.GetFiles(sourceDir))
{
    var attr = File.GetAttributes(filePath);
    if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden)
        continue; // Пропускаем скрытые файлы

    // Остальной код копирования
}

Ошибки длинных путей

Windows долгое время ограничивала длину пути примерно 260 символами. В современных версиях это ограничение можно снять, но если вы работаете со старыми системами, длинные пути могут быть проблемой.

Символические ссылки и «джанкшены»

В специфичных сценариях в директориях могут встречаться символические ссылки или «junctions». Обычные методы копирования могут копировать их как обычные папки или игнорировать. Для большинства учебных задач это неважно, но если работаете с системными каталогами — будьте внимательнее.

2
Задача
C# SELF, 40 уровень, 2 лекция
Недоступна
Рекурсивное копирование папок
Рекурсивное копирование папок
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 59
19 февраля 2026
Валидатор докопался до того, что "сообщение недостаточно информативное". Начал давать цикличные рекомендации, которые противоречили друг другу.