[c#] C #에서 디렉터리의 전체 내용을 복사하는 방법?


Answers

흠, 나는 그 질문을 오해한다고 생각하지만 나는 그것을 위험에 빠뜨릴 것입니다. 다음과 같은 간단한 방법에있어 문제점은 무엇입니까?

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

편집 이 게시물은 똑같이 간단한 질문에 대한 간단한 대답에 대한 인상적인 수의 downvotes를 얻었으므로 설명을 추가하겠습니다. downvoting하기 전에 이것을 읽으십시오 .

우선, 이 코드는 질문의 코드를 대체하는 것이 아닙니다 . 설명 목적으로 만 사용됩니다.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory 는이 답변에서 누락 된 추가 정확성 테스트 (예 : 원본과 대상이 유효한 디렉터리인지, 원본이 대상의 부모인지 여부 등)를 수행합니다. 이 코드는 아마 더 최적화 된 것입니다.

즉, 코드 는 잘 작동합니다 . 그것은 (거의 동일하게) 수년 동안 성숙한 소프트웨어에서 사용되어 왔습니다. 모든 IO 처리와 관련된 고유 한 변덕 (예 : 사용자가 코드를 쓰는 동안 USB 드라이브를 수동으로 뽑으면 어떻게됩니까?)과 관련하여 알려진 문제가 없습니다.

특히 여기서 재귀를 사용하는 것이 문제가 아님을 지적하고자합니다. 이론적으로 (개념적으로, 그것은 가장 우아한 해결책입니다) 실제적으로도 : 이 코드는 스택을 오버플로하지 않습니다 . 스택은 깊이 중첩 된 파일 계층을 처리 할만큼 충분히 큽니다. 스택 공간이 문제가되기 오래 전에 폴더 경로 길이 제한이 시작됩니다.

악의적 인 사용자 는 각각 하나의 문자로 된 중첩 된 디렉토리를 사용하여이 가정을 깨뜨릴 수 있습니다. 나는 이것을 시도하지 않았다. 그러나 요점을 설명하기 위해 : 일반적인 컴퓨터에서이 코드 오버플로를 만들기 위해 디렉터리를 수천 번 중첩시켜야합니다. 이것은 단순히 현실적인 시나리오가 아닙니다.

Question

C #의 한 위치에서 다른 위치로 디렉토리의 전체 내용을 복사하려고합니다.

많은 재귀없이 System.IO 클래스를 사용하여이 작업을 수행하는 방법이없는 것 같습니다.

Microsoft.VisualBasic 에 대한 참조를 추가하면 VB에서 사용할 수있는 메서드가 있습니다.

new Computer().FileSystem.CopyDirectory(sourceFolder, outputFolder);

이것은 오히려 추한 해킹처럼 보인다. 더 좋은 방법이 있습니까?




다음은 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;
        }
    }
}



모든 코드 (재귀가있는 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;
        }
    }



이전 코드를 사용해 주셔서 죄송 합니다만, 버그가 여전히 남아 있습니다 : ((가장 빠른 총 문제로 떨어졌습니다) 여기서는 테스트를 거쳐 작동합니다. 핵심은 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);
    }
}



모든 폴더 및 파일을 복사하기위한 하나의 루프가있는 하나의 변형 :

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



콘래드의 인기있는 답변이 마음에 드는데 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;
}



이 시도:

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 인수는 다양하지만 아이디어를 얻을 수 있습니다.




이 사이트는 항상 나를 많이 도와 주었고, 이제는 내가 아는 것들로 다른 사람들을 돕는 것이 나의 차례입니다.

나는 아래 코드가 누군가에게 유용하기를 희망한다.

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



다음은 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;
}



Related



Tags

c# c#   .net .net   copy