2012-08-13 37 views
5

Có cách nào hiệu quả hơn để điền danh sách tên tệp từ một thư mục có bộ lọc ngày không?C# GetFiles với Bộ lọc Ngày

Hiện nay, tôi đang làm điều này:

foreach (FileInfo flInfo in directory.GetFiles()) 
{ 
    DateTime yesterday = DateTime.Today.AddDays(-1); 
    String name = flInfo.Name.Substring(3,4); 
    DateTime creationTime = flInfo.CreationTime; 
    if (creationTime.Date == yesterday.Date) 
     yesterdaysList.Add(name); 
} 

này đi qua tất cả các file trong thư mục, và tôi cảm thấy như không nên có một hơn cách hiệu quả.

+0

Bạn có thể thử sử dụng LINQ. – Bernard

Trả lời

5

Tôi nghĩ rằng bạn là sau khi nhận được hiệu quả cao hơn ở cấp hệ thống tập tin, chứ không phải ở cấp C#. Nếu trường hợp đó là câu trả lời là no: Không có cách nào để thông báo cho hệ thống tệp lọc theo ngày. Nó sẽ không cần thiết trả lại mọi thứ.

Nếu bạn đang sử dụng hiệu quả của CPU: Đây là các mục becilerdding vô nghĩa với một hộp danh sách cực kỳ đắt hơn lọc ngày tháng. Tối ưu hóa mã của bạn sẽ không mang lại kết quả nào.

16

Giải pháp thứ nhất:

Bạn có thể sử dụng LINQ:

List<string> yesterdaysList = directory.GetFiles().Where(x => x.CreationTime.Date == DateTime.Today.AddDays(-1)) 
                .Select(x => x.Name) 
                .ToList(); 

Sau đó, bạn có thể sử dụng trực tiếp danh sách này các tên.

Second Giải pháp:

Một giải pháp khác để làm cho nó nhanh hơn có thể là:

DateTime yesterday = DateTime.Today.AddDays(-1); //initialize this variable only one time 

foreach (FileInfo flInfo in directory.GetFiles()){ 
    if (flInfo.CreationTime.Date == yesterday.Date) //use directly flInfo.CreationTime and flInfo.Name without create another variable 
     yesterdaysList.Add(flInfo.Name.Substring(3,4)); 
} 

Benchmark:

tôi đã làm một chuẩn mực bằng cách sử dụng mã này:

class Program { 
    static void Main(string[ ] args) { 
     DirectoryInfo directory = new DirectoryInfo(@"D:\Films"); 
     Stopwatch timer = new Stopwatch(); 
     timer.Start(); 

     for (int i = 0; i < 100000; i++) { 
      List<string> yesterdaysList = directory.GetFiles().Where(x => x.CreationTime.Date == DateTime.Today.AddDays(-1)) 
               .Select(x => x.Name) 
               .ToList(); 
     } 

     timer.Stop(); 
     TimeSpan elapsedtime = timer.Elapsed; 
     Console.WriteLine(string.Format("{0:00}:{1:00}:{2:00}", elapsedtime.Minutes, elapsedtime.Seconds, elapsedtime.Milliseconds/10)); 
     timer.Restart(); 

     DateTime yesterday = DateTime.Today.AddDays(-1); //initialize this variable only one time 
     for (int i = 0; i < 100000; i++) { 
      List<string> yesterdaysList = new List<string>(); 

      foreach (FileInfo flInfo in directory.GetFiles()) { 
       if (flInfo.CreationTime.Date == yesterday.Date) //use directly flInfo.CreationTime and flInfo.Name without create another variable 
        yesterdaysList.Add(flInfo.Name.Substring(3, 4)); 
      } 
     } 


     timer.Stop(); 
     elapsedtime = timer.Elapsed; 
     Console.WriteLine(string.Format("{0:00}:{1:00}:{2:00}", elapsedtime.Minutes, elapsedtime.Seconds, elapsedtime.Milliseconds/10)); 
     timer.Restart(); 

     for (int i = 0; i < 100000; i++) { 
      List<string> list = new List<string>(); 

      foreach (FileInfo flInfo in directory.GetFiles()) { 
       DateTime _yesterday = DateTime.Today.AddDays(-1); 
       String name = flInfo.Name.Substring(3, 4); 
       DateTime creationTime = flInfo.CreationTime; 
       if (creationTime.Date == _yesterday.Date) 
        list.Add(name); 
      } 
     } 

     elapsedtime = timer.Elapsed; 
     Console.WriteLine(string.Format("{0:00}:{1:00}:{2:00}", elapsedtime.Minutes, elapsedtime.Seconds, elapsedtime.Milliseconds/10)); 
    } 
} 

Kết quả:

First solution: 00:19:84 
Second solution: 00:17:64 
Third solution: 00:19:91 //Your solution 
+0

Làm cách nào hiệu quả hơn? – svick

+3

LINQ kém hiệu quả hơn so với foreach. Nó là sạch hơn và dễ đọc hơn, nhưng đang tạo ra cùng một vòng lặp đằng sau hậu trường và thêm chi phí của chính nó. –

+0

Ok, tôi đã chỉnh sửa mã của mình, tôi đã thêm giải pháp khác và tôi đã làm điểm chuẩn. –

4

Tôi không cảm thấy muốn tạo đủ tệp với ngày tạo chính xác để làm điểm chuẩn phong nha, vì vậy tôi đã làm một phiên bản tổng quát hơn có thời gian bắt đầu và kết thúc và cung cấp tên tệp phù hợp. Làm cho nó cung cấp cho một chuỗi con cụ thể của các tập tin được tạo ra ngày hôm qua sau tự nhiên từ đó.

Các tinh khiết NET câu trả lời đơn luồng nhanh nhất tôi đã đưa ra là:

private static IEnumerable<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated) 
{ 
    foreach(FileInfo fi in new DirectoryInfo(directory).GetFiles()) 
     if(fi.CreationTime >= minCreated && fi.CreationTime <= maxCreated) 
      yield return fi.Name; 
} 

tôi dự kiến ​​sẽ có EnumerateFiles() là nhanh hơn một chút, nhưng nó bật ra hơi chậm (có thể làm tốt hơn nếu bạn đi qua một mạng, nhưng tôi đã không kiểm tra điều đó).

Có một tăng nhẹ với:

private static ParallelQuery<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated) 
{ 
    return new DirectoryInfo(directory).GetFiles().AsParallel() 
     .Where(fi => fi.CreationTime >= minCreated && fi.CreationTime <= maxCreated) 
     .Select(fi => fi.Name); 
} 

Nhưng không nhiều vì nó không giúp cuộc gọi thực tế để GetFiles(). Nếu bạn không có lõi để sử dụng, hoặc không có một kết quả đủ lớn từ GetFiles() sau đó nó sẽ chỉ làm cho mọi thứ tồi tệ hơn (các chi phí của AsParallel() lớn hơn lợi ích của việc lọc song song).Mặt khác, nếu bạn có thể thực hiện các bước xử lý tiếp theo cũng song song, thì tốc độ ứng dụng tổng thể có thể cải thiện.

Có vẻ như không có điểm nào làm điều này với EnumerateFiles() vì nó dường như không song song tốt, bởi vì nó dựa trên cùng một cách tiếp cận mà tôi sắp đến, và đó là kết nối trước đó - cần kết quả trước đó để tạo ra kế tiếp.

nhanh nhất tôi nhận được:

public const int MAX_PATH = 260; 
public const int MAX_ALTERNATE = 14; 

[StructLayoutAttribute(LayoutKind.Sequential)] 
public struct FILETIME 
{ 
    public uint dwLowDateTime; 
    public uint dwHighDateTime; 
    public static implicit operator long(FILETIME ft) 
    { 
     return (((long)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; 
    } 
}; 

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] 
public struct WIN32_FIND_DATA 
{ 
    public FileAttributes dwFileAttributes; 
    public FILETIME ftCreationTime; 
    public FILETIME ftLastAccessTime; 
    public FILETIME ftLastWriteTime; 
    public uint nFileSizeHigh; 
    public uint nFileSizeLow; 
    public uint dwReserved0; 
    public uint dwReserved1; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)] 
    public string cFileName; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_ALTERNATE)] 
    public string cAlternate; 
} 

[DllImport("kernel32", CharSet=CharSet.Unicode)] 
public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData); 

[DllImport("kernel32", CharSet=CharSet.Unicode)] 
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData); 

[DllImport("kernel32.dll")] 
public static extern bool FindClose(IntPtr hFindFile); 

private static IEnumerable<string> FilesWithinDates(string directory, DateTime minCreated, DateTime maxCreated) 
{ 
    long startFrom = minCreated.ToFileTimeUtc(); 
    long endAt = maxCreated.ToFileTimeUtc(); 
    WIN32_FIND_DATA findData; 
    IntPtr findHandle = FindFirstFile(@"\\?\" + directory + @"\*", out findData); 
    if(findHandle != new IntPtr(-1)) 
    { 
     do 
     { 
      if(
       (findData.dwFileAttributes & FileAttributes.Directory) == 0 
       && 
       findData.ftCreationTime >= startFrom 
       && 
       findData.ftCreationTime <= endAt 
      ) 
      { 
       yield return findData.cFileName; 
      } 
     } 
     while(FindNextFile(findHandle, out findData)); 
     FindClose(findHandle); 
    } 
} 

Nó dicey không có mà FindClose() hứa bởi một IDisposable và thực hiện cuộn bằng tay của IEnumerator<string> nên không chỉ làm cho điều đó dễ dàng hơn để làm gì (lý do nghiêm trọng để thực hiện điều đó) nhưng cũng hy vọng cạo đi như 3 nano giây hoặc một cái gì đó (không phải là một lý do nghiêm trọng để làm điều đó), nhưng ở trên cho thấy ý tưởng cơ bản.

+0

Bạn có thể xử lý 'FindClose()' bằng cách đặt nó vào 'cuối cùng'. Các khối 'finally' xuất hiện được thực hiện khi' Dispose() 'của toán tử được gọi (mà' foreach' tự động thực hiện). – svick

+0

@svick Bạn thực sự có thể. Trong một số trường hợp có thể có những cạm bẫy với cách tiếp cận đó (thường xuyên nhất nếu điều tra viên không thực sự được liệt kê), nhưng bạn đúng - đây không phải là một trong số chúng. –

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