2009-09-02 6 views
2

올바르게 수행하고 있는지 또는 내 논리가 올바른지 확실하지 않습니다.Traversing 폴더 트리에서 파일을 삭제하는 방법

폴더 구조를 삭제하려고하는데 특정 일 수보다 오래된 파일을 삭제하려고합니다.이 부분은 올바르게 구현되었으므로 빈 폴더를 삭제합니다.

이 모든 작업을 하나의 루프로 수행 할 수 있습니까?
어디에서 폴더를 삭제합니까?

최대 3 또는 4 레벨 아래로 빈 폴더를 삭제하고 싶습니다.

private static void TraverseTree(System.IO.DirectoryInfo folder, double days) 
    { 
     Stack<string> dirs = new Stack<string>(); 

     if (!folder.Exists) 
      throw new ArgumentException(); 

     dirs.Push(folder.FullName); 

     while (dirs.Count > 0) 
     { 
      string currentDir = dirs.Pop(); 
      string[] subDirs; 
      try 
      { 
       subDirs = System.IO.Directory.GetDirectories(currentDir); 
      } 
      // An UnauthorizedAccessException exception will be thrown if we do not have 
      // discovery permission on a folder or file. It may or may not be acceptable 
      // to ignore the exception and continue enumerating the remaining files and 
      // folders. It is also possible (but unlikely) that a DirectoryNotFound exception 
      // will be raised. This will happen if currentDir has been deleted by 
      // another application or thread after our call to Directory.Exists. The 
      // choice of which exceptions to catch depends entirely on the specific task 
      // you are intending to perform and also on how much you know with certainty 
      // about the systems on which this code will run. 
      catch (UnauthorizedAccessException e) 
      { 
       Console.WriteLine(e.Message); 
       continue; 
      } 
      catch (System.IO.DirectoryNotFoundException e) 
      { 
       Console.WriteLine(e.Message); 
       continue; 
      } 

      string[] files = null; 
      try 
      { 
       files = System.IO.Directory.GetFiles(currentDir); 
      } 
      catch (UnauthorizedAccessException e) 
      { 

       Console.WriteLine(e.Message); 
       continue; 
      } 
      catch (System.IO.DirectoryNotFoundException e) 
      { 
       Console.WriteLine(e.Message); 
       continue; 
      } 

      // Perform the required action on each file here. 
      // Modify this block to perform your required task. 
      foreach (string file in files) 
      { 
       try 
       { 
        // Perform whatever action is required in your scenario. 
        System.IO.FileInfo fi = new System.IO.FileInfo(file); 
        Console.WriteLine("{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime); 

        // Delete old files 
        if (fi.LastWriteTime < DateTime.Now.AddDays(-days)) 
         fi.Delete(); 
       } 
       catch (System.IO.FileNotFoundException e) 
       { 
        // If file was deleted by a separate application 
        // or thread since the call to TraverseTree() 
        // then just continue. 
        Console.WriteLine(e.Message); 
        continue; 
       } 
      } 

      // Push the subdirectories onto the stack for traversal. 
      // This could also be done before handing the files. 
      foreach (string str in subDirs) 
       dirs.Push(str); 
     } 
    } 

코드 MSDN에서입니다.

답변

5

재귀 적 접근은 아마 더 깨끗합니다. 당신 워커는 IEnumerable로 비 재귀 적으로 구현 된 파일 시스템을 제공하는 문제에

private static void DeleteOldFilesAndFolders(string path) 
{ 
    foreach (string directory in System.IO.Directory.GetDirectories(path)) 
    { 
     DeleteOldFilesAndFolders(directory); 

     // If the directory is empty and old, delete it here. 
    } 

    foreach (string file in System.IO.Directory.GetFiles(path)) 
    { 
     // Check the file's age and delete it if it's old. 
    } 
} 
+0

재귀 파일 시스템 워커를 주면 스택 오버플로 (overflow) 파일 시스템을 제공 할 것입니다. – plinth

+0

@ plinth 정말요? 파일 시스템의 깊이는 어느 정도입니까? 정말로 수백 개의 폴더를 파묻힌 파일이 있습니까? –

+0

예. 스택을 n으로 설정하면, n + 1 폴더를 임베드하고 그것이 왜 그런지에 대한 "합리적인"유스 케이스를 제공합니다. 몇 년 전, 저는 Acrobat Catalogue를 작업했고, 그런 상황에 대비하여 글 머리 기호를 사용해야했습니다. 힙 할당은 구현하기가 다소 어려우며 리소스가 부족할 때 배기가 더 어려워지고 트랩하기 쉽습니다. 현실 세계에서 가장 좋은 솔루션은 무엇입니까? – plinth

0

Here is a more general solution. 솔루션 아마로 구현 될 수있는

: 나는 당신의 코드에 대해 알

List<string> directoriesToDelete = new List<string>(); 
DirectoryWalker walker = new DirectoryWalker(@"C:\pathToSource\src", 
    dir => { 
     if (Directory.GetFileSystemEntries(dir).Length == 0) { 
      directoriesToDelete.Add(dir); 
      return false; 
     } 
     return true; 
    }, 
    file => { 
     if (FileIsTooOld(file)) { 
      return true; 
     } 
     return false; 
    } 
    ); 
foreach (string file in walker) 
    File.Delete(file); 
foreach (string dir in directoriesToDelete) 
    Directory.Delete(dir); 
3

뭔가 완전히 트리 구조를 산책 "메커니즘"라인의 수십 코드의 두 줄을 압도한다는 것입니다 그것은 실제로 작업을 수행합니다. 따라서이 코드를 읽고, 이해하고, 변경하고, 디버그하고 유지하는 것을 어렵게 만듭니다.

다음은 내가 대신 할 작업입니다.

프로그램에 상위 수준 조작이 3 개 있습니다. (1) 모든 파일 가져 오기, (2) 삭제할 필터 찾기, (3) 각 파일 삭제. 그래서 각각의 문장을 하나의 문장으로 작성하는 프로그램을 작성하십시오.

첫 번째 작업에서는 위의 메커니즘을 자체 함수로 분해합니다. 예를 들어 IEnumerable을 구현하는 함수이며 모든 작업은 파일에 대한 정보를 생성합니다. 그것은 을 수행하지 않습니다. 그들과 관련된 모든 것; 유일한 목적은 파일 정보를 뱉어내는 것입니다.

일단이 메커니즘을 사용하면 두 번째 작업을 수행하기 위해 해당 순서의 맨 위에 쿼리를 작성할 수 있습니다. 세 번째 작업은 두 번째 작업에서 바로 이어집니다.

var allFiles = TraverseFolder(folder); 
var filesToDelete = from file in allFiles where IsOld(file) select file; 
foreach(var fileToDelete in filesToDelete) Delete(fileToDelete); 

이 분명하다 : 짧은에서

, 프로그램의 메인 라인의 모양은? 내 질문을 게시하기 전에 게시물을보고 않았다

/* Given a directory path and a datetime, 
* recursively delete all files and directories contained in such directory 
* (given directory included) that are younger than the given date. 
*/ 
private bool DeleteDirectoryTree(string dir, DateTime keepFilesYoungerThan) 
{ 
    //checks 
    if (String.IsNullOrEmpty(dir) || !Directory.Exists(dir)) 
     return false; 

    //recurse on children directories 
    foreach (string childDir in Directory.GetDirectories(dir)) 
     DeleteDirectoryTree(childDir, keepFilesYoungerThan); 

    //loop on children files 
    foreach (string file in Directory.GetFiles(dir)) 
    { 
     //calculate file datetime 
     DateTime fileDT = new DateTime(Math.Max(File.GetCreationTime(file).Ticks, File.GetLastWriteTime(file).Ticks)); 
     //if file is old, delete it 
     if (fileDT <= keepFilesYoungerThan) 
      try 
      { 
       File.Delete(file); 
       Log("Deleted file " + file); 
      } 
      catch (Exception e) 
      { 
       LogError("Could not delete file " + file + ", cause: " + e.Message); 
      } 
    } 

    //if this directory is empty, delete it 
    if (!Directory.EnumerateFileSystemEntries(dir).Any()) 
     try 
     { 
      Directory.Delete(dir); 
      Log("Deleted directory " + dir); 
     } 
     catch (Exception e) 
     { 
      LogError("Could not delete directory " + dir + ", cause: " + e.Message); 
     } 

    return true; 
} 
+0

+1 예, 매우 명확합니다. 감사 – Picflight

관련 문제