Nie miałem ochoty tworzyć wystarczającej liczby plików z odpowiednią datą utworzenia, aby zrobić przyzwoity test porównawczy, więc zrobiłem bardziej ogólną wersję, która zajmuje początek i koniec czasu i podaje nazwy pasujących do siebie plików. Sprawienie, by nadawało określony fragment plików utworzonych wczoraj, wynika oczywiście z tego.
Najszybszy jednowątkowy czysta NET odpowiedź wymyśliłem było:
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;
}
Liczyłam EnumerateFiles()
się nieco szybciej, ale okazało się nieco wolniej (może to zrobić lepiej, jeśli jesteś przechodzenie przez sieć, ale tego nie przetestowałem).
Jest niewielki zysk z:
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);
}
Ale nie za wiele, ponieważ to nie pomaga rzeczywiste wezwanie do GetFiles()
. Jeśli nie masz rdzeni do użycia, lub nie jest wystarczająco duży wynik z GetFiles()
, to po prostu pogorszy to sytuację (koszty ogólne z AsParallel()
są większe niż korzyści wynikające z jednoczesnego filtrowania).Z drugiej strony, jeśli możesz równolegle wykonywać kolejne kroki przetwarzania, ogólna szybkość aplikacji może się poprawić.
Wydaje się, że nie ma sensu robić tego z EnumerateFiles()
, ponieważ nie wydaje się, aby dobrze paraliżować, ponieważ opiera się na tym samym podejściu, które zamierzam osiągnąć, a to jest z natury szeregowe - potrzebuję poprzedniego wyniku, aby wyprodukować Kolejny.
Najszybszy Dostałem:
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);
}
}
To ryzykowny nie mający że FindClose()
obiecane przez IDisposable
i realizacja ręcznie zwijane z IEnumerator<string>
powinny nie tylko sprawiają, że łatwiej zrobić (poważny powód, by to zrobić), ale miejmy nadzieję, że zgaśnie jak 3 nanosekundy lub coś w tym rodzaju (nie jest to poważny powód), ale powyższe pokazuje podstawowy pomysł.
Możesz spróbować użyć LINQ. – Bernard