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).
"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? –
Có bao nhiêu tệp trong thư mục? –
@Karlsen Trong mỗi thư mục là một tệp. – user278618