569 votes

Copier l'intégralité du contenu d'un répertoire en C#

Je souhaite copier l'intégralité du contenu d'un répertoire d'un emplacement à un autre en C#.

Il ne semble pas y avoir de moyen de faire cela en utilisant System.IO sans beaucoup de récursion.

Il y a une méthode en VB que nous pouvons utiliser si nous ajoutons une référence à Microsoft.VisualBasic :

new Microsoft.VisualBasic.Devices.Computer().
    FileSystem.CopyDirectory( sourceFolder, outputFolder );

Cela semble être un hack plutôt moche. Existe-t-il un meilleur moyen ?

108 votes

Je dirais qu'en regardant les alternatives postées ci-dessous, la méthode VB n'est pas si moche.

2 votes

La vraie question est de savoir pourquoi cela ne fait pas partie de la bibliothèque IO par défaut. À l'heure actuelle, nous avons probablement tous mis le même code dans notre bibliothèque personnelle.

601voto

tboswell Points 1812

Beaucoup plus facile

private static void CopyFilesRecursively(string sourcePath, string targetPath)
{
    //Now Create all of the directories
    foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
    {
        Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
    }

    //Copy all the files & Replaces any files with the same name
    foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories))
    {
        File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
    }
}

0 votes

Bonne idée - je ne sais pas pourquoi je n'ai jamais pensé à utiliser SearchOption.AllDirectories . J'utiliserais probablement le SubString plutôt que Replace mais c'est juste une question de style de codage.

31 votes

C'est un beau morceau de code, mais ce n'est pas le genre de code qui peut être utilisé partout. Les développeurs doivent faire attention car dirPath.Replace pourrait avoir des conséquences indésirables. Juste un avertissement aux personnes qui aiment faire des copier-coller sur le net. Le code posté par @jaysponsored est plus sûr parce qu'il n'utilise pas string.Replace mais je suis sûr qu'il a aussi ses cas particuliers.

0 votes

C'est génial ! Cela résout mon problème. Mais y a-t-il un moyen de copier/déplacer des fichiers avec des milliards de chiffres et sans supprimer les fichiers/dossiers existants ?

254voto

Konrad Rudolph Points 231505

Hmm, je crois que j'ai mal compris la question mais je vais m'y risquer. Quel est le problème avec la méthode simple suivante ?

public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) {
    foreach (DirectoryInfo dir in source.GetDirectories())
        CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
    foreach (FileInfo file in source.GetFiles())
        file.CopyTo(Path.Combine(target.FullName, file.Name));
}

EDIT Puisque ce message a recueilli un nombre impressionnant de votes négatifs pour une réponse aussi simple à une question tout aussi simple, permettez-moi d'ajouter une explication. S'il vous plaît Lisez ceci avant de voter à la baisse .

Tout d'abord, Ce code n'est pas destiné à être remplacé par un autre code. au code de la question. Il s'agit uniquement d'une illustration.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory effectue quelques tests d'exactitude supplémentaires (par exemple, si la source et la cible sont des répertoires valides, si la source est un parent de la cible, etc.) qui sont absents de cette réponse. Ce code est probablement aussi plus optimisé.

Cela dit, le code fonctionne bien . Il s'agit de a (presque identique) est utilisé dans un logiciel mature depuis des années. En dehors de l'inconstance inhérente à toute gestion des entrées/sorties (par exemple, que se passe-t-il si l'utilisateur débranche manuellement la clé USB alors que votre code est en train d'écrire dessus ?

En particulier, j'aimerais souligner que l'utilisation de la récursion ici n'est absolument pas un problème. Ni en théorie (conceptuellement, c'est la solution la plus élégante), ni en pratique : ce code ne débordera pas de la pile . La pile est suffisamment grande pour gérer même les hiérarchies de fichiers profondément imbriquées. Bien avant que l'espace de la pile ne devienne un problème, la limitation de la longueur du chemin du dossier entre en jeu.

Notez qu'un utilisateur malveillant Il est possible de briser cette hypothèse en utilisant des répertoires profondément imbriqués d'une lettre chacun. Je n'ai pas essayé. Mais juste pour illustrer le point : afin de faire déborder ce code sur un ordinateur typique, les répertoires devraient être imbriqués plusieurs fois. mille temps. Ce n'est tout simplement pas un scénario réaliste.

0 votes

Rien, à part l'appel récursif. Pourquoi devons-nous faire ça en C# ?

5 votes

C'est la récursion de tête. Elle peut être la proie d'un débordement de pile si les répertoires sont imbriqués assez profondément.

24 votes

Jusqu'à très récemment, la profondeur d'imbrication des répertoires était limitée par le système d'exploitation. Je doute que vous trouviez des répertoires qui sont imbriqués plus de quelques centaines de fois (si même). Le code ci-dessus peut prendre beaucoup plus.

144voto

Justin R. Points 10122

Copié de MSDN :

using System;
using System.IO;

class CopyDir
{
    public static void Copy(string sourceDirectory, string targetDirectory)
    {
        DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
        DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);

        CopyAll(diSource, diTarget);
    }

    public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
    {
        Directory.CreateDirectory(target.FullName);

        // Copy each file into the new directory.
        foreach (FileInfo fi in source.GetFiles())
        {
            Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
            fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
        }

        // Copy each subdirectory using recursion.
        foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
        {
            DirectoryInfo nextTargetSubDir =
                target.CreateSubdirectory(diSourceSubDir.Name);
            CopyAll(diSourceSubDir, nextTargetSubDir);
        }
    }

    public static void Main()
    {
        string sourceDirectory = @"c:\sourceDirectory";
        string targetDirectory = @"c:\targetDirectory";

        Copy(sourceDirectory, targetDirectory);
    }

    // Output will vary based on the contents of the source directory.
}

11 votes

Il n'y a aucune raison de vérifier si le répertoire existe, appelez simplement Directoty.CreateDirectory qui ne fera rien si le répertoire existe déjà.

1 votes

Pour ceux qui souhaitent traiter les chemins de plus de 256 caractères, vous pouvez utiliser un paquet Nuget appelé ZetaLongPaths.

3 votes

Cette réponse semble être la plus utile de toutes. L'utilisation de DirectoryInfo au lieu de chaînes de caractères permet d'éviter de nombreux problèmes potentiels.

52voto

Josef Points 4395

Ou, si vous voulez employer la manière forte, ajoutez une référence à votre projet pour Microsoft.VisualBasic et utilisez ensuite ce qui suit :

Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(fromDirectory, toDirectory);

Toutefois, il est préférable d'utiliser l'une des fonctions récursives, car elle ne devra pas charger la dll VB.

1 votes

Ce n'est pas vraiment différent de la façon dont je l'ai fait de toute façon - vous devez toujours charger le matériel de compatibilité descendante de VB afin de pouvoir le faire.

11 votes

Le chargement de l'assemblage VB est-il coûteux ? Les options VB sont beaucoup plus élégantes que les versions C#.

3 votes

Qu'est-ce que "les trucs de rétrocompatibilité de VB" ? CopyDirectory utilise soit le Shell, soit le Framework.

49voto

d4nt Points 4486

Essayez ça :

Process proc = new Process();
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "xcopy.exe");
proc.StartInfo.Arguments = @"C:\source C:\destination /E /I";
proc.Start();

Vos arguments de xcopy peuvent varier mais vous avez l'idée.

0 votes

Que signifient les /E /I ? Écraser ?

3 votes

/E lui demande de copier tous les sous-répertoires (même les vides). /I lui indique que si la destination n'existe pas, il faut créer un répertoire avec ce nom.

6 votes

Ajoutez des guillemets pour être sûr.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X