C#でディレクトリの内容全体をコピーする


524

ディレクトリのコンテンツ全体をある場所から別の場所にC#でコピーしたいと思います。

System.IO多くの再帰なしにクラスを使用してこれを行う方法はないようです。

への参照を追加する場合に使用できるVBのメソッドがありますMicrosoft.VisualBasic

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

これはやや醜いハックのようです。もっと良い方法はありますか?


101
以下に掲載されている代替案を見ると、VBの方法はそれほど醜く見えません。
Kevin Kershaw

41
それが.NET Frameworkの一部である場合、どのようにハッキングすることができますか?コードを書くのをやめて、得たものを使ってください。
AMissico、2009

15
それはよくある誤解です。Microsft.VisualBasicには、VBでのコーディングを非常に簡単にする一般的なVisual Basicプロシージャがすべて含まれています。Microsot.VisualBasic.Compatibilityは、VB6レガシーに使用されるアセンブリです。
AMissico 2009

63
Microsoft.VisualBasic.Devices.Computer.FileSystemには2,000行を超えるコードがあります。CopyDirectoryは、親フォルダーを子フォルダーにコピーしたり、その他のチェックを行わないことを保証します。高度に最適化されています。選択された答えはせいぜい壊れやすいコードです。
AMissico 2009

17
@AMissico-わかりました、では、なぜこの最適化された完全なコードが含まれMicrosoft.VisualBasicているのSystem.IOですか?それがMonoにない理由は、「コア」と見なされるすべてのライブラリーがそうであるということですSystem.[something]-他のすべてのライブラリーはそうではありません。追加のDLLを参照しても問題はありませんが、Microsoftがこの機能をに含めていないのには十分な理由がありますSystem.IO
Keith

回答:


553

はるかに簡単

//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", 
    SearchOption.AllDirectories))
    Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));

//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, DestinationPath), true);

25
それは確かに素晴らしいコードですが、これはどこでも使用できる種類のコードではありません。dirPath.Replaceは望ましくない結果を引き起こす可能性があるため、開発者は注意する必要があります。ネット上でコピーアンドペーストするのが好きな人への警告です。@jaysponsoredによって投稿されたコードはstring.Replaceを使用しないため、より安全ですが、コーナーケースもあると確信しています。
Alex

17
ターゲットディレクトリがすでに存在する場合は例外がスローされるため、このコードには注意してください。また、既存のファイルは上書きされません。各ディレクトリを作成する前にチェックを追加し、File.Copyのオーバーロードを使用して、ターゲットファイルが存在する場合はそれを上書きします。
joerage

30
@Xaisoft- Replaceパス内に繰り返しパターンがある場合は問題に"sourceDir/things/sourceDir/things"なります。たとえば、になるはずですが"destinationDir/things/sourceDir/things"、replaceを使用すると、次のようになります"destinationDir/things/destinationDir/things"
Keith

35
なぜ*.*代わりに*?拡張子のないファイルもコピーしたくないですか?
ダリル

10
何かをビルドして、オープンソース.NET Coreに貢献しましょう...:/
Mzn

231

うーん、私は質問を誤解していると思いますが、私はそれを危険にさらすつもりです。次の簡単な方法の何が問題になっていますか?

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

編集この投稿は、同様に単純な質問に対するそのような単純な答えに対する印象的な多数の反対投票を獲得したので、説明を付け加えましょう。反対票を投じる前にこれを読んでください

まず第一に、このコードは問題のコードのドロップイン置換として意図されていません。説明のみを目的としています。

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryこの回答から欠落している追加の正当性テスト(たとえば、ソースとターゲットが有効なディレクトリかどうか、ソースがターゲットの親であるかどうかなど)を行います。そのコードもおそらくより最適化されています。

つまり、コードはうまく機能します。それ(ほとんど同じ)何年も成熟したソフトウェアで使用されてきました。すべてのIO処理に存在する固有の気まぐれさ(たとえば、コードがUSBドライブに書き込んでいる間にユーザーが手動でUSBドライブを抜くとどうなるか)以外は、既知の問題はありません。

特に、ここでの再帰の使用はまったく問題ではないことを指摘しておきます。理論的には(概念的には、これは最もエレガントなソリューションです)、実際にはこのコードはスタックをオーバーフローしません。スタックは、深くネストされたファイル階層を処理するのに十分な大きさです。スタックスペースが問題になるずっと前に、フォルダーパスの長さの制限が適用されます。

悪意のあるユーザーが、1文字ずつの深くネストされたディレクトリを使用することで、この想定を破ることができる可能性があることに注意してください。私はこれを試していません。ただし、要点を説明すると、このコードを一般的なコンピューターでオーバーフローさせるには、ディレクトリを数回ネストする必要があります。これは単に現実的なシナリオではありません。


5
これはヘッドの再帰です。ディレクトリが十分にネストされている場合、スタックオーバーフローの犠牲になる可能性があります。
スポールソン2008

19
ごく最近まで、ディレクトリのネストの深さはOSによって制限されていました。数百回以上ネストされているディレクトリが見つかるとは思えません。上記のコードは取ることができ非常に多くを。
Konrad Rudolph

5
私は再帰的なアプローチが好きです。スタックオーバーフローのリスクは最低でも最小限です。
デビッドバサラブ

49
@DTashkinov:すみませんが、それは少し過剰に思えます。なぜ明らかなコード==反対投票なのですか?逆のことが当てはまるはずです。組み込みメソッドは既に投稿されていましたが、キースは別のメソッドを特に求めました。また、最後の文はどういう意味ですか?申し訳ありませんが、反対票を投じた理由がまったくわかりません。
Konrad Rudolph、

6
@AMissico:より良いです?フレームワークのVBコードよりも優れていると主張した人はいません。私たちは知っていることはありません。
Konrad Rudolph、

132

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.
}

8
ディレクトリが存在するかどうかを確認する理由はありません。ディレクトリが既に存在する場合は何もしないDirectoty.CreateDirectoryを呼び出します。
Tal Jerome、

1
256文字を超えるパスを処理する場合は、ZetaLongPathsと呼ばれるNugetパッケージを使用できます
AK

2
この答えは、それらすべての中で最も役立つようです。文字列の代わりにDirectoryInfoを使用することにより、多くの潜在的な問題が回避されます。
DaedalusAlpha

50

これを試して:

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

xcopyの引数は異なる場合がありますが、アイデアはわかります。


3
/ Eは、すべてのサブディレクトリ(空のものも含む)をコピーするように指示します。/ Iは、宛先が存在しない場合、その名前のディレクトリを作成することを伝えます。
d4nt

6
安全のために二重引用符を追加します。
jaysonragasa

6
/ Yを追加して、既存のファイルを上書きするように求められないようにします。stackoverflow.com/q/191209/138938
Jon Crowell

16
申し訳ありませんが、これは恐ろしいことです。ターゲットシステムはWindowsであると想定しています。将来のバージョンでは、特定のパスにxcopy.exeが含まれていると想定しています。xcopyのパラメーターは変更されないと想定しています。xcopyのパラメーターを文字列としてアセンブルする必要があるため、多くのエラーが発生する可能性があります。また、このサンプルでは、​​他の方法とは対照的にこれが黙って失敗するため、開始されたプロセスの結果のエラー処理については触れていません。
セルシャープ

3
@MatthiasJansen、あなたは非常に個人的なものだと思います。答えは要点であり、それを達成する方法について多くを説明しています...質問はクロスプラットフォームの互換性を要求しないか、またはxcopyなどを使用しないので、ポスターはこれがどのようにして達成できるかを説明するために答えただけです...そこ同じことを行うには1000の方法があり、答えはさまざまです。そのため、このフォーラムで対処し、世界中のプログラマがここで経験を共有します。私はあなたのコメントに反対票を投じます。
KMX 2016年

47

または、難しい方法を実行する場合は、Microsoft.VisualBasicのプロジェクトへの参照を追加してから、以下を使用します。

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

ただし、VB dllをロードする必要がないため、再帰関数の1つを使用する方が適切です。


1
それはとにかく私がそれをやった方法と本当に違いはありません-それを行うことができるようにするためにあなたはまだVBの後方互換性のものをロードする必要があります。
キース

10
VBアセンブリのロードは高価ですか?VBオプションは、C#バージョンよりもはるかにエレガントです。
jwmiller5 2009年

3
「VBの下位互換性」とは何ですか?CopyDirectoryは、シェルまたはフレームワークのいずれかを使用します。
AMissico、2009

3
オンSystem.IO.Directoryになっているといいのですが、書き換えるよりはましです。
Josh M.

2
これは、他のどのオプションよりもはるかに簡単にイモに行く方法です
reggaeguitar

38

このサイトはいつも私を助けてくれました。そして今、私が知っていることで他の人を助けるために私の番です。

以下の私のコードが誰かのために役立つことを願っています。

string source_dir = @"E:\";
string destination_dir = @"C:\";

// substring is to remove destination_dir absolute path (E:\).

// Create subdirectory structure in destination    
    foreach (string dir in System.IO.Directory.GetDirectories(source_dir, "*", System.IO.SearchOption.AllDirectories))
    {
        System.IO.Directory.CreateDirectory(System.IO.Path.Combine(destination_dir, dir.Substring(source_dir.Length + 1)));
        // Example:
        //     > C:\sources (and not C:\E:\sources)
    }

    foreach (string file_name in System.IO.Directory.GetFiles(source_dir, "*", System.IO.SearchOption.AllDirectories))
    {
        System.IO.File.Copy(file_name, System.IO.Path.Combine(destination_dir, file_name.Substring(source_dir.Length + 1)));
    }

1
末尾のバックスラッシュを思い出してください
Alexey F

24
フォーク、使用Path.Combine()。ファイルパスをまとめるために文字列連結を使用しないでください。
アンディ

3
上記のコードスニペットにOBOBがあります。source_dir.Length + 1ではなく、を使用する必要がありsource_dir.Lengthます。
PellucidWombat

このコードは良い概念ですが...ファイルに「。」がなくてもかまいません。その中で、ystem.IO.Directory.GetFiles(source_dir、 "*"、System.IO.SearchOption.AllDirectories))
Jean Libera

@JeanLibera、ありがとうございます。私はあなたの提案でコードを変更しました。
jaysponsored

14

スタックオーバーフローを回避するために、再帰せずにフォルダーを再帰的にコピーします。

public static void CopyDirectory(string source, string target)
{
    var stack = new Stack<Folders>();
    stack.Push(new Folders(source, target));

    while (stack.Count > 0)
    {
        var folders = stack.Pop();
        Directory.CreateDirectory(folders.Target);
        foreach (var file in Directory.GetFiles(folders.Source, "*.*"))
        {
            File.Copy(file, Path.Combine(folders.Target, Path.GetFileName(file)));
        }

        foreach (var folder in Directory.GetDirectories(folders.Source))
        {
            stack.Push(new Folders(folder, Path.Combine(folders.Target, Path.GetFileName(folder))));
        }
    }
}

public class Folders
{
    public string Source { get; private set; }
    public string Target { get; private set; }

    public Folders(string source, string target)
    {
        Source = source;
        Target = target;
    }
}

便利な非再帰テンプレート:)
Minh Nguyen

2
パス制限を超える前にスタックを爆破することは想像に
Ed S.

5

これは、このようなIOタスクに使用したユーティリティクラスです。

using System;
using System.Runtime.InteropServices;

namespace MyNameSpace
{
    public class ShellFileOperation
    {
        private static String StringArrayToMultiString(String[] stringArray)
        {
            String multiString = "";

            if (stringArray == null)
                return "";

            for (int i=0 ; i<stringArray.Length ; i++)
                multiString += stringArray[i] + '\0';

            multiString += '\0';

            return multiString;
        }

        public static bool Copy(string source, string dest)
        {
            return Copy(new String[] { source }, new String[] { dest });
        }

        public static bool Copy(String[] source, String[] dest)
        {
            Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();

            FileOpStruct.hwnd = IntPtr.Zero;
            FileOpStruct.wFunc = (uint)Win32.FO_COPY;

            String multiSource = StringArrayToMultiString(source);
            String multiDest = StringArrayToMultiString(dest);
            FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
            FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);

            FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
            FileOpStruct.lpszProgressTitle = "";
            FileOpStruct.fAnyOperationsAborted = 0;
            FileOpStruct.hNameMappings = IntPtr.Zero;

            int retval = Win32.SHFileOperation(ref FileOpStruct);

            if(retval != 0) return false;
            return true;
        }

        public static bool Move(string source, string dest)
        {
            return Move(new String[] { source }, new String[] { dest });
        }

        public static bool Delete(string file)
        {
            Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();

            FileOpStruct.hwnd = IntPtr.Zero;
            FileOpStruct.wFunc = (uint)Win32.FO_DELETE;

            String multiSource = StringArrayToMultiString(new string[] { file });
            FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
            FileOpStruct.pTo =  IntPtr.Zero;

            FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_SILENT | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION | (ushort)Win32.ShellFileOperationFlags.FOF_NOERRORUI | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMMKDIR;
            FileOpStruct.lpszProgressTitle = "";
            FileOpStruct.fAnyOperationsAborted = 0;
            FileOpStruct.hNameMappings = IntPtr.Zero;

            int retval = Win32.SHFileOperation(ref FileOpStruct);

            if(retval != 0) return false;
            return true;
        }

        public static bool Move(String[] source, String[] dest)
        {
            Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();

            FileOpStruct.hwnd = IntPtr.Zero;
            FileOpStruct.wFunc = (uint)Win32.FO_MOVE;

            String multiSource = StringArrayToMultiString(source);
            String multiDest = StringArrayToMultiString(dest);
            FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
            FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);

            FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
            FileOpStruct.lpszProgressTitle = "";
            FileOpStruct.fAnyOperationsAborted = 0;
            FileOpStruct.hNameMappings = IntPtr.Zero;

            int retval = Win32.SHFileOperation(ref FileOpStruct);

            if(retval != 0) return false;
            return true;
        }
    }
}

MicrosoftはMicrosoft.VisualBasicに対して内部的にSHFileOperationを使用していることに注意してください。
jrh

3

パフォーマンスに対応していない可能性がありますが、30MBのフォルダに使用しており、問題なく動作します。さらに、私はそのような簡単なタスクに必要なコードと再帰の量がすべて好きではありませんでした。

var source_folder = "c:\src";
var dest_folder = "c:\dest";
var zipFile = source_folder + ".zip";

ZipFile.CreateFromDirectory(source_folder, zipFile);
ZipFile.ExtractToDirectory(zipFile, dest_folder);
File.Delete(zipFile);

注:ZipFileは、System.IO.Compression名前空間の.NET 4.5以降で使用できます。


1
私も、したがって質問もしませんが、選択された回答は再帰を必要としません。この答えは、ディスク上にzipファイルを作成します。これは、ファイルコピーの追加作業の多くです。データの追加コピーを作成するだけでなく、データの圧縮と解凍にプロセッサ時間を費やしています。きっとうまくいくと思います。靴で爪をたたくのと同じ方法ですが、もっとうまくいく方法がありますが、失敗する可能性のあるものはもっとたくさんあります。
キース

私がこれで終わった理由は文字列の置換です。他の人が指摘したように、受け入れられた答えには多くの懸念があります。ジャンクションリンクが機能しない可能性があります。また、拡張子や名前のないフォルダーパターンやファイルが繰り返される可能性もあります。コードが少ないほど、失敗する可能性が低くなります。そして、プロセッサ時間は私にとって問題ではないので、それは私の特定のケースに適しています
AlexanderD

2
ええ、それは単一の信号を回避するためにあなたの道を1000マイル運転するようなものですが、それはあなたの旅なので、それのために行きます。フォルダパターンのチェックは、ZIPが内部で実行する必要があることと比較して簡単です。プロセッサ、ディスク、電力を無駄にしないことを気にする人、または同じマシンで他のプログラムと一緒に実行する必要がある場合は、これを強くお勧めします。また、面接でこの種の質問をされたことがある場合は、「私のコードは単純なので、プロセッサ時間は気にしない」と決してやりません-あなたは仕事を得られません。
キース

1
@ justin-rの回答に切り替えました。それでも、私はそれを行うためのもうひとつの方法として、そこにこの答えを残しておきます
AlexanderD

1
フォルダーが別のネットワーク共有にあり、多くのファイルが含まれている場合、これは私の意見では最良のオプションです。
ダニーパーカー

2

サーバーと開発マシンで作業している場合は、おそらくエラーをチェックし、xcopyパスを変更する必要がないため、d4ntの回答を少し改善しました。

public void CopyFolder(string source, string destination)
{
    string xcopyPath = Environment.GetEnvironmentVariable("WINDIR") + @"\System32\xcopy.exe";
    ProcessStartInfo info = new ProcessStartInfo(xcopyPath);
    info.UseShellExecute = false;
    info.RedirectStandardOutput = true;
    info.Arguments = string.Format("\"{0}\" \"{1}\" /E /I", source, destination);

    Process process = Process.Start(info);
    process.WaitForExit();
    string result = process.StandardOutput.ReadToEnd();

    if (process.ExitCode != 0)
    {
        // Or your own custom exception, or just return false if you prefer.
        throw new InvalidOperationException(string.Format("Failed to copy {0} to {1}: {2}", source, destination, result));
    }
}

2

これは私のコードがこの助けを願っています

    private void KCOPY(string source, string destination)
    {
        if (IsFile(source))
        {
            string target = Path.Combine(destination, Path.GetFileName(source));
            File.Copy(source, target, true);
        }
        else
        {
            string fileName = Path.GetFileName(source);
            string target = System.IO.Path.Combine(destination, fileName);
            if (!System.IO.Directory.Exists(target))
            {
                System.IO.Directory.CreateDirectory(target);
            }

            List<string> files = GetAllFileAndFolder(source);

            foreach (string file in files)
            {
                KCOPY(file, target);
            }
        }
    }

    private List<string> GetAllFileAndFolder(string path)
    {
        List<string> allFile = new List<string>();
        foreach (string dir in Directory.GetDirectories(path))
        {
            allFile.Add(dir);
        }
        foreach (string file in Directory.GetFiles(path))
        {
            allFile.Add(file);
        }

        return allFile;
    }
    private bool IsFile(string path)
    {
        if ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory)
        {
            return false;
        }
        return true;
    }

SearchOptionフォルダーとファイルの検索にフラグを使用することにより、選択された回答を表示します。これは4行のコードで行われます。また.HasFlag、列挙型の拡張機能も確認してください。
キース

2

Konradの人気のある回答が気に入ったが、sourceそれ自体をフォルダーの下targetに置くのではなく、フォルダーにしたい場合targetは、次のコードを使用します。新しく作成されたが返されますDirectoryInfo。これは便利です。

public static DirectoryInfo CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
  var newDirectoryInfo = target.CreateSubdirectory(source.Name);
  foreach (var fileInfo in source.GetFiles())
    fileInfo.CopyTo(Path.Combine(newDirectoryInfo.FullName, fileInfo.Name));

  foreach (var childDirectoryInfo in source.GetDirectories())
    CopyFilesRecursively(childDirectoryInfo, newDirectoryInfo);

  return newDirectoryInfo;
}

2

MicrosoftのWebサイトから取得した、これをいつでも使用できます。

static void Main()
{
    // Copy from the current directory, include subdirectories.
    DirectoryCopy(".", @".\temp", true);
}

private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
    // Get the subdirectories for the specified directory.
    DirectoryInfo dir = new DirectoryInfo(sourceDirName);

    if (!dir.Exists)
    {
        throw new DirectoryNotFoundException(
            "Source directory does not exist or could not be found: "
            + sourceDirName);
    }

    DirectoryInfo[] dirs = dir.GetDirectories();
    // If the destination directory doesn't exist, create it.
    if (!Directory.Exists(destDirName))
    {
        Directory.CreateDirectory(destDirName);
    }

    // Get the files in the directory and copy them to the new location.
    FileInfo[] files = dir.GetFiles();
    foreach (FileInfo file in files)
    {
        string temppath = Path.Combine(destDirName, file.Name);
        file.CopyTo(temppath, false);
    }

    // If copying subdirectories, copy them and their contents to new location.
    if (copySubDirs)
    {
        foreach (DirectoryInfo subdir in dirs)
        {
            string temppath = Path.Combine(destDirName, subdir.Name);
            DirectoryCopy(subdir.FullName, temppath, copySubDirs);
        }
    }
}

1
これはすばらしいfile.CopyTo(temppath, false);ことです。「このファイルが存在しない場合にのみ、この場所にこのファイルをコピーしてください」という行が含まれていることに注意してください。しかし、なぜデフォルトになっているのかは理解できます。ファイルを上書きするメソッドにフラグを追加することもできます。
アンディ

2

tboswellはプルーフバージョンを置き換えます(ファイルパス内の繰り返しパターンに耐性があります)。

public static void copyAll(string SourcePath , string DestinationPath )
{
   //Now Create all of the directories
   foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories))
      Directory.CreateDirectory(Path.Combine(DestinationPath ,dirPath.Remove(0, SourcePath.Length ))  );

   //Copy all the files & Replaces any files with the same name
   foreach (string newPath in Directory.GetFiles(SourcePath, "*.*",  SearchOption.AllDirectories))
      File.Copy(newPath, Path.Combine(DestinationPath , newPath.Remove(0, SourcePath.Length)) , true);
    }

3
フォーク、使用Path.Combine()。ファイルパスをまとめるために文字列連結を使用しないでください。
アンディ

2

私の解決策は基本的に@Termininjaの回答を変更したものですが、少し拡張し、受け入れられた回答よりも5倍以上速いようです。

public static void CopyEntireDirectory(string path, string newPath)
{
    Parallel.ForEach(Directory.GetFileSystemEntries(path, "*", SearchOption.AllDirectories)
    ,(fileName) =>
    {
        string output = Regex.Replace(fileName, "^" + Regex.Escape(path), newPath);
        if (File.Exists(fileName))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(output));
            File.Copy(fileName, output, true);
        }
        else
            Directory.CreateDirectory(output);
    });
}

編集:@Ahmed Sabryを完全な並列foreachに変更するとより良い結果が得られますが、コードは再帰関数を使用するため、状況によっては理想的ではありません。

public static void CopyEntireDirectory(DirectoryInfo source, DirectoryInfo target, bool overwiteFiles = true)
{
    if (!source.Exists) return;
    if (!target.Exists) target.Create();

    Parallel.ForEach(source.GetDirectories(), (sourceChildDirectory) =>
        CopyEntireDirectory(sourceChildDirectory, new DirectoryInfo(Path.Combine(target.FullName, sourceChildDirectory.Name))));

    Parallel.ForEach(source.GetFiles(), sourceFile =>
        sourceFile.CopyTo(Path.Combine(target.FullName, sourceFile.Name), overwiteFiles));
}

1

以前のコードで申し訳ありませんが、バグはまだありました:((最速の銃の問題の餌食になりました)。ここでテストされて機能しています。キーはSearchOption.AllDirectoriesであり、明示的な再帰の必要性を排除します。

string path = "C:\\a";
string[] dirs = Directory.GetDirectories(path, "*.*", SearchOption.AllDirectories);
string newpath = "C:\\x";
try
{
    Directory.CreateDirectory(newpath);
}
catch (IOException ex)
{
    Console.WriteLine(ex.Message);
}
for (int j = 0; j < dirs.Length; j++)
{
    try
    {
        Directory.CreateDirectory(dirs[j].Replace(path, newpath));
    }
    catch (IOException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

string[] files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
for (int j = 0; j < files.Length; j++)            
{
    try
    {
        File.Copy(files[j], files[j].Replace(path, newpath));
    }
    catch (IOException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

1

FileInfo.CopyToの DirectoryInfoの拡張メソッドを次に示しoverwriteます(パラメーターに注意してください)。

public static DirectoryInfo CopyTo(this DirectoryInfo sourceDir, string destinationPath, bool overwrite = false)
{
    var sourcePath = sourceDir.FullName;

    var destination = new DirectoryInfo(destinationPath);

    destination.Create();

    foreach (var sourceSubDirPath in Directory.EnumerateDirectories(sourcePath, "*", SearchOption.AllDirectories))
        Directory.CreateDirectory(sourceSubDirPath.Replace(sourcePath, destinationPath));

    foreach (var file in Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories))
        File.Copy(file, file.Replace(sourcePath, destinationPath), overwrite);

    return destination;
}

1

このクラスを使用します。

public static class Extensions
{
    public static void CopyTo(this DirectoryInfo source, DirectoryInfo target, bool overwiteFiles = true)
    {
        if (!source.Exists) return;
        if (!target.Exists) target.Create();

        Parallel.ForEach(source.GetDirectories(), (sourceChildDirectory) => 
            CopyTo(sourceChildDirectory, new DirectoryInfo(Path.Combine(target.FullName, sourceChildDirectory.Name))));

        foreach (var sourceFile in source.GetFiles())
            sourceFile.CopyTo(Path.Combine(target.FullName, sourceFile.Name), overwiteFiles);
    }
    public static void CopyTo(this DirectoryInfo source, string target, bool overwiteFiles = true)
    {
        CopyTo(source, new DirectoryInfo(target), overwiteFiles);
    }
}

1
これは他の回答に似ており、使用するためにリファクタリングされ.ToList().ForEach((これは、ディレクトリを直接列挙するよりも少し作業、メモリが多く、少し遅い)、拡張メソッドとして使用されます。選択した回答はSearchOption.AllDirectories再帰を使用して回避するため、そのモデルに切り替えることをお勧めします。また、通常、拡張メソッドで型の名前を必要としない-名前を変更して、次のCopyTo()ようになったsourceDir.CopyTo(destination);
Keith

1

すべてのフォルダーとファイルをコピーするためのループが1つだけある1つのバリアント:

foreach (var f in Directory.GetFileSystemEntries(path, "*", SearchOption.AllDirectories))
{
    var output = Regex.Replace(f, @"^" + path, newPath);
    if (File.Exists(f)) File.Copy(f, output, true);
    else Directory.CreateDirectory(output);
}

を使用するRegex場合は、おそらくRegex.Escape(path)式の構成の一部としても使用する必要があります(特にWindowsのパス区切り文字を考慮してください)。あなたは可能性も作成(そしておそらくコンパイル)から利益を得るnew Regex()ループのオブジェクト外にではなく、静的な方法に頼ります。
jimbobmcgee

0

どのコードよりも優れている(再帰によるDirectoryInfoの拡張メソッド)

public static bool CopyTo(this DirectoryInfo source, string destination)
    {
        try
        {
            foreach (string dirPath in Directory.GetDirectories(source.FullName))
            {
                var newDirPath = dirPath.Replace(source.FullName, destination);
                Directory.CreateDirectory(newDirPath);
                new DirectoryInfo(dirPath).CopyTo(newDirPath);
            }
            //Copy all the files & Replaces any files with the same name
            foreach (string filePath in Directory.GetFiles(source.FullName))
            {
                File.Copy(filePath, filePath.Replace(source.FullName,destination), true);
            }
            return true;
        }
        catch (IOException exp)
        {
            return false;
        }
    }

1
これが受け入れられた答えに何を追加するのかはわかりませんが、再帰を使用して(必要がない場合)例外を非表示にしてデバッグを困難にすることは除きます。
キース

0

フォルダーのすべてのファイルをコピーして置き換える

        public static void CopyAndReplaceAll(string SourcePath, string DestinationPath, string backupPath)
    {
            foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories))
            {
                Directory.CreateDirectory($"{DestinationPath}{dirPath.Remove(0, SourcePath.Length)}");
                Directory.CreateDirectory($"{backupPath}{dirPath.Remove(0, SourcePath.Length)}");
            }
            foreach (string newPath in Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories))
            {
                if (!File.Exists($"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}"))
                    File.Copy(newPath, $"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}");
                else
                    File.Replace(newPath
                        , $"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}"
                        , $"{ backupPath}{newPath.Remove(0, SourcePath.Length)}", false);
            }
    }

答えを乾杯しますが、これが何を追加するのかわかりません。また、try catch throw無意味です。
キース、

0

以下のコードは、Microsoftの提案であるか・ツー・コピーのディレクトリ と、それは親愛なるで共有されている@iato ますが、ソースフォルダの単なるコピーサブディレクトリとファイルを再帰的それ自身のフォルダのソースをコピーしません(右クリックのような- >コピーを)。

しかし、この答えの下にはトリッキーな方法あります:

private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs = true)
        {
            // Get the subdirectories for the specified directory.
            DirectoryInfo dir = new DirectoryInfo(sourceDirName);

            if (!dir.Exists)
            {
                throw new DirectoryNotFoundException(
                    "Source directory does not exist or could not be found: "
                    + sourceDirName);
            }

            DirectoryInfo[] dirs = dir.GetDirectories();
            // If the destination directory doesn't exist, create it.
            if (!Directory.Exists(destDirName))
            {
                Directory.CreateDirectory(destDirName);
            }

            // Get the files in the directory and copy them to the new location.
            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string temppath = Path.Combine(destDirName, file.Name);
                file.CopyTo(temppath, false);
            }

            // If copying subdirectories, copy them and their contents to new location.
            if (copySubDirs)
            {
                foreach (DirectoryInfo subdir in dirs)
                {
                    string temppath = Path.Combine(destDirName, subdir.Name);
                    DirectoryCopy(subdir.FullName, temppath, copySubDirs);
                }
            }
        }

ソースフォルダーとサブフォルダーの内容を再帰的にコピーする場合は、次のように使用できます

string source = @"J:\source\";
string dest= @"J:\destination\";
DirectoryCopy(source, dest);

しかし、ソースディレクトリを自分でコピーする場合(ソースフォルダを右クリックして[コピー]をクリックし、次にコピー先のフォルダで[貼り付け]をクリックした場合と同様)、次のように使用する必要があります。

 string source = @"J:\source\";
 string dest= @"J:\destination\";
 DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));

以下の回答がすでに投稿されています:stackoverflow.com/a/45199038/1951524
Martin Schneider

@ MA-Maddinに感謝しますが、ソースフォルダー自体をコピーしますか?または単に内容?
Arash.Zandi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.