2013-07-23 31 views
7

Chào buổi sáng tất cả, Tôi có một thư mục chứa hàng nghìn thư mục con ở các độ sâu khác nhau. Tôi cần liệt kê tất cả các thư mục không chứa thư mục con (ngôn ngữ "cuối dòng"). Sẽ tốt nếu chúng chứa các tệp. Có cách nào để làm điều này với EnumerateDirectories?C# Liệt kê tất cả thư mục con "lá" với EnumerateDirectories

Ví dụ, nếu một EnumerateDirectories đầy đủ đệ quy quay trở lại:

/files/ 
/files/q 
/files/q/1 
/files/q/2 
/files/q/2/examples 
/files/7 
/files/7/eb 
/files/7/eb/s 
/files/7/eb/s/t 

Tôi chỉ quan tâm đến:

/files/q/1 
/files/q/2/examples 
/files/7/eb/s/t 

Trả lời

14

này nên làm việc:

var folderWithoutSubfolder = Directory.EnumerateDirectories(root, "*.*", SearchOption.AllDirectories) 
    .Where(f => !Directory.EnumerateDirectories(f, "*.*", SearchOption.TopDirectoryOnly).Any()); 
+3

truy cập bình chọn để loại bỏ -1 ... dường như làm việc cho tôi – Sayse

+0

+1, mà chỉ làm cho tôi tự hào ! –

+0

đẹp One-liners – m1m1k

3

Nếu bạn muốn tránh gọi EnumerateDirectories() hai lần cho mỗi thư mục, bạn có thể thực hiện nó như vậy:

public IEnumerable<string> EnumerateLeafFolders(string root) 
{ 
    bool anySubfolders = false; 

    foreach (var subfolder in Directory.EnumerateDirectories(root)) 
    { 
     anySubfolders = true; 

     foreach (var leafFolder in EnumerateLeafFolders(subfolder)) 
      yield return leafFolder; 
    } 

    if (!anySubfolders) 
     yield return root; 
} 

tôi đã làm một số xét nghiệm thời gian, và cho tôi phương pháp này là hơn hai lần nhanh như sử dụng phương pháp LINQ.

Tôi đã chạy thử nghiệm này bằng cách sử dụng bản xây dựng bản phát hành, chạy bên ngoài bất kỳ trình gỡ lỗi nào. Tôi chạy nó trên một ổ SSD có chứa một số lượng lớn các thư mục - tổng số thư mục LEAF là 25035.

kết quả của tôi cho chạy thứ hai của chương trình (chạy đầu tiên là để làm nóng bộ nhớ cache đĩa):

Calling Using linq. 1 times took 00:00:08.2707813 
Calling Using yield. 1 times took 00:00:03.6457477 
Calling Using linq. 1 times took 00:00:08.0668787 
Calling Using yield. 1 times took 00:00:03.5960438 
Calling Using linq. 1 times took 00:00:08.1501002 
Calling Using yield. 1 times took 00:00:03.6589386 
Calling Using linq. 1 times took 00:00:08.1325582 
Calling Using yield. 1 times took 00:00:03.6563730 
Calling Using linq. 1 times took 00:00:07.9994754 
Calling Using yield. 1 times took 00:00:03.5616040 
Calling Using linq. 1 times took 00:00:08.0803573 
Calling Using yield. 1 times took 00:00:03.5892681 
Calling Using linq. 1 times took 00:00:08.1216921 
Calling Using yield. 1 times took 00:00:03.6571429 
Calling Using linq. 1 times took 00:00:08.1437973 
Calling Using yield. 1 times took 00:00:03.6606362 
Calling Using linq. 1 times took 00:00:08.0058955 
Calling Using yield. 1 times took 00:00:03.6477621 
Calling Using linq. 1 times took 00:00:08.1084669 
Calling Using yield. 1 times took 00:00:03.5875057 

Như bạn có thể thấy, sử dụng phương pháp tiếp cận năng suất nhanh hơn đáng kể. (Có lẽ vì nó không liệt kê từng thư mục hai lần.)

mã kiểm tra của tôi:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 

namespace Demo 
{ 
    class Program 
    { 
     private void run() 
     { 
      string root = "F:\\TFROOT"; 

      Action test1 =() => leafFolders1(root).Count(); 
      Action test2 =() => leafFolders2(root).Count(); 

      for (int i = 0; i < 10; ++i) 
      { 
       test1.TimeThis("Using linq."); 
       test2.TimeThis("Using yield."); 
      } 
     } 

     static void Main() 
     { 
      new Program().run(); 
     } 

     static IEnumerable<string> leafFolders1(string root) 
     { 
      var folderWithoutSubfolder = Directory.EnumerateDirectories(root, "*.*", SearchOption.AllDirectories) 
       .Where(f => !Directory.EnumerateDirectories(f, "*.*", SearchOption.TopDirectoryOnly).Any()); 

      return folderWithoutSubfolder; 
     } 

     static IEnumerable<string> leafFolders2(string root) 
     { 
      bool anySubfolders = false; 

      foreach (var subfolder in Directory.EnumerateDirectories(root)) 
      { 
       anySubfolders = true; 

       foreach (var leafFolder in leafFolders2(subfolder)) 
        yield return leafFolder; 
      } 

      if (!anySubfolders) 
       yield return root; 
     } 
    } 

    static class DemoUtil 
    { 
     public static void Print(this object self) 
     { 
      Console.WriteLine(self); 
     } 

     public static void Print(this string self) 
     { 
      Console.WriteLine(self); 
     } 

     public static void Print<T>(this IEnumerable<T> self) 
     { 
      foreach (var item in self) 
       Console.WriteLine(item); 
     } 

     public static void TimeThis(this Action action, string title, int count = 1) 
     { 
      var sw = Stopwatch.StartNew(); 

      for (int i = 0; i < count; ++i) 
       action(); 

      Console.WriteLine("Calling {0} {1} times took {2}", title, count, sw.Elapsed); 
     } 
    } 
} 
+0

EnumerateDirectories được đánh giá rất lười biếng, do đó, cuộc gọi thêm được thực hiện trong câu trả lời của Tim là khá rẻ. Khi tôi chuẩn bị mã của bạn với Tim, Tim đã chạy trong thời gian ngắn hơn một nửa. Tôi tưởng tượng điều này là bởi vì bằng cách sử dụng một iterator đệ quy cho biết thêm rất nhiều chi phí. – Brian

+0

@Brian Bạn đã chạy thử nghiệm nhiều lần để loại bỏ hiện vật gây ra bởi bộ nhớ đệm đĩa chạy đầu tiên thông qua? –

+0

Có. Tôi chạy các bài kiểm tra một vài trăm lần trước khi tôi bắt đầu, biên soạn với tối ưu hóa, và chạy mỗi bài kiểm tra một lần (để tránh hiện vật jitter) trước khi bắt đầu hẹn giờ. – Brian

Các vấn đề liên quan