2010-04-26 79 views
5

Tôi có chương trình ghi vào cơ sở dữ liệu có thư mục nào đầy hoặc trống. Bây giờ tôi đang sử dụngKiểm tra xem thư mục có tập tin hay không

bool hasFiles=false; 
(Directory.GetFiles(path).Length >0) ? hasFiles=true: hasFiles=false; 

nhưng phải mất gần một giờ và tôi không thể làm gì trong thời gian này.

Có cách nào nhanh nhất để kiểm tra xem thư mục có tệp nào không?

+2

"it" mất một giờ? Dòng mã cụ thể này, hoặc sử dụng nó trong vòng lặp qua hàng nghìn thư mục trên đĩa của bạn? –

+0

Có bao nhiêu tệp trong thư mục? –

+0

@Karlsen Trong mỗi thư mục là một tệp. – user278618

Trả lời

5

Chìa khóa để tăng tốc tìm kiếm mạng chéo như vậy là cắt giảm số lượng yêu cầu trên mạng. Thay vì nhận tất cả các thư mục, sau đó kiểm tra từng thư mục, hãy thử và nhận mọi thứ từ một cuộc gọi.

Trong .NET 3.5 không có cách nào đệ quy để nhận tất cả các tệp và thư mục, do đó bạn phải tự xây dựng nó (xem bên dưới). Trong .NET 4 quá tải mới tồn tại đến điều này trong một bước.

Sử dụng DirectoryInfo một tài khoản cũng nhận được thông tin về việc tên được trả về có phải là tệp hoặc thư mục cũng cắt giảm cuộc gọi hay không.

Điều này có nghĩa tách một danh sách tất cả các thư mục và các tập tin trở thành một cái gì đó như thế này:

struct AllDirectories { 
    public List<string> DirectoriesWithoutFiles { get; set; } 
    public List<string> DirectoriesWithFiles { get; set; } 
} 

static class FileSystemScanner { 
    public AllDirectories DivideDirectories(string startingPath) { 
    var startingDir = new DirectoryInfo(startingPath); 

    // allContent IList<FileSystemInfo> 
    var allContent = GetAllFileSystemObjects(startingDir); 
    var allFiles = allContent.Where(f => !(f.Attributes & FileAttributes.Directory)) 
          .Cast<FileInfo>(); 
    var dirs = allContent.Where(f => (f.Attributes & FileAttributes.Directory)) 
         .Cast<DirectoryInfo>(); 
    var allDirs = new SortedList<DirectoryInfo>(dirs, new FileSystemInfoComparer()); 

    var res = new AllDirectories { 
     DirectoriesWithFiles = new List<string>() 
    }; 
    foreach (var file in allFiles) { 
     var dirName = Path.GetDirectoryName(file.Name); 
     if (allDirs.Remove(dirName)) { 
     // Was removed, so first time this dir name seen. 
     res.DirectoriesWithFiles.Add(dirName); 
     } 
    } 
    // allDirs now just contains directories without files 
    res.DirectoriesWithoutFiles = new List<String>(addDirs.Select(d => d.Name)); 
    } 

    class FileSystemInfoComparer : IComparer<FileSystemInfo> { 
    public int Compare(FileSystemInfo l, FileSystemInfo r) { 
     return String.Compare(l.Name, r.Name, StringComparison.OrdinalIgnoreCase); 
    } 
    } 
} 

Thực hiện GetAllFileSystemObjects phụ thuộc vào phiên bản .NET. Trên .NET 4 nó là rất dễ dàng:

ILIst<FileSystemInfo> GetAllFileSystemObjects(DirectoryInfo root) { 
    return root.GetFileSystemInfos("*.*", SearchOptions.AllDirectories); 
} 

Trong các phiên bản trước đó một chút nhiều việc phải làm:

ILIst<FileSystemInfo> GetAllFileSystemObjects(DirectoryInfo root) { 
    var res = new List<FileSystemInfo>(); 
    var pending = new Queue<DirectoryInfo>(new [] { root }); 

    while (pending.Count > 0) { 
    var dir = pending.Dequeue(); 
    var content = dir.GetFileSystemInfos(); 
    res.AddRange(content); 
    foreach (var dir in content.Where(f => (f.Attributes & FileAttributes.Directory)) 
           .Cast<DirectoryInfo>()) { 
     pending.Enqueue(dir); 
    } 
    } 

    return res; 
} 

Cách tiếp cận này gọi vào hệ thống tập tin như vài lần càng tốt, chỉ cần một lần trên NET 4 hoặc một lần cho mỗi thư mục trên các phiên bản trước đó, cho phép máy khách và máy chủ mạng giảm thiểu số lượng các cuộc gọi hệ thống tập tin cơ bản và các chuyến đi vòng quanh mạng.

Nhận FileSystemInfo trường hợp có bất lợi khi cần nhiều thao tác hệ thống tệp (tôi tin rằng điều này phụ thuộc vào hệ điều hành), nhưng đối với mỗi tên, bất kỳ giải pháp nào cũng cần biết đó là tệp hay thư mục. (mà không cần đến P/Gọi của FindFileFirst/FindNextFile/FindClose).


Bên cạnh, bên trên sẽ dễ dàng hơn với một phương pháp mở rộng phân vùng:

Tuple<IEnumerable<T>,IEnumerable<T>> Extensions.Partition<T>(
               this IEnumerable<T> input, 
               Func<T,bool> parition); 

Viết rằng để được lười biếng sẽ là một bài tập thú vị (chỉ tiêu thụ đầu vào khi một cái gì đó lặp trên một trong những kết quả đầu ra, trong khi đệm phần còn lại).

+0

Cần một cái gì đó tương tự như thế này nhưng chỉ là tự hỏi. Khi bạn sử dụng biến 'addDirs', tôi đoán bạn có nghĩa là' allDirs'? Hay tôi đang thiếu một cái gì đó? – Niklas

+0

@Niklas có thể. (Nhưng nó đã được một vài năm ...) Hãy nhớ rằng bạn không cần mã này trong .NET 4 vì nó có thể đọc các tập tin và thư mục đệ quy. – Richard

3

Tôi giả sử (mặc dù tôi không biết chắc chắn) bởi vì bạn đang gọi GetFiles() trên ổ đĩa mạng, nó sẽ tăng thời gian đáng kể để truy xuất tất cả các tệp từ tất cả thư mục 30k và liệt kê chúng.

Tôi đã tìm thấy một Điều tra danh bạ thay thế here trên CodeProject có vẻ đầy hứa hẹn.

Cách khác ... bạn có thể tạo WebService trên máy chủ liệt kê mọi thứ cho bạn và trả về kết quả sau đó.

EDIT: Tôi nghĩ rằng vấn đề của bạn có nhiều khả năng là truy cập thư mục. Mỗi khi bạn truy cập vào một thư mục trong ổ đĩa mạng, bạn sẽ được kiểm tra bảo mật và quyền. Đó * 30k thư mục sẽ là một hit hiệu suất lớn. Tôi rất nghi ngờ khi sử dụng FindFirstFile sẽ giúp ích nhiều khi số tệp thực tế được liệt kê sẽ chỉ bao giờ là 0 hoặc 1.

0

Đặt cược tốt nhất của bạn là sử dụng hàm API FindFirstFile. Nó sẽ không mất gần như lâu sau đó.

+1

Mỗi thư mục chỉ có một tệp; vấn đề * trông * là số lượng lớn các thư mục * từ xa *, được truy cập tuần tự. –

+0

+1 Đây là một cuộc thảo luận nơi ai đó phát hiện ra rằng FindFirstfile nhanh hơn rất nhiều so với Directories.GetFiles để kiểm tra các thư mục trống có giá trị như vậy: http://stackoverflow.com/questions/755574/how-to-quickly-check-if- folder-is-empty-net –

+1

Tôi đồng ý với Marc ở đây. Vấn đề không liệt kê các tập tin, nó liệt kê và bước qua tất cả các cấu trúc thư mục. Mỗi lần .Net gọi GetFiles() trên một thư mục, sẽ có một loạt các kiểm tra bảo mật mỗi khi Directory có quyền truy cập đã cố gắng trên nó. – GenericTypeTea

2

Có thể là đáng nói:

nhưng phải mất gần một giờ, và tôi không thể làm bất cứ điều gì trong thời gian này. (nhấn mạnh thêm)

Bạn đang làm điều này từ ứng dụng GUI, trên luồng chính? Nếu có, hãy nhổ quá trình này bằng cách sử dụng BackgroundWorker. Ít nhất thì ứng dụng sẽ tiếp tục phản hồi. Bạn cũng có thể thêm séc cho CancellationPending trong phương thức và hủy séc nếu quá trình này mất quá nhiều thời gian.

Loại tiếp tuyến cho câu hỏi của bạn - chỉ là điều tôi nhận thấy và nghĩ rằng tôi sẽ nhận xét.

4

Nếu bạn đang sử dụng .Net 4.0 có xem xét phương pháp EnumerateFiles. http://msdn.microsoft.com/en-us/library/dd413232(v=VS.100).aspx

Các EnumerateFiles và GetFiles phương pháp khác nhau như sau: Khi bạn EnumerateFiles sử dụng, bạn có thể bắt đầu liệt kê bộ sưu tập của FileInfo đối tượng trước khi toàn bộ bộ sưu tập được trả về; khi bạn sử dụng GetFiles, bạn phải chờ cho toàn bộ mảng đối tượng FileInfo được trả về trước khi bạn có thể truy cập mảng. Do đó, khi bạn đang làm việc với nhiều tệp và thư mục, EnumerateFiles có thể hiệu quả hơn.

Bằng cách này không phải tất cả các tập tin được lấy ra từ thư mục, nếu điều tra viên có ít nhất 1 tập tin thư mục không rỗng

9

Để kiểm tra xem bất kỳ tập tin tồn tại bên trong các thư mục hoặc thư mục con, trong. net 4, bạn có thể sử dụng phương pháp bên dưới:

public bool isDirectoryContainFiles(string path) { 
    if (!Directory.Exists(path)) return false; 
    return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).Any(); 
} 
Các vấn đề liên quan