No tuve ganas de crear suficientes archivos con la fecha de creación correcta para hacer un punto de referencia decente, así que hice una versión más general que toma una hora de inicio y finalización y da los nombres de los archivos que coinciden. Hacerlo dar una subcadena particular de archivos creados ayer sigue naturalmente de eso.
La respuesta más rápida .NET pura de un solo subproceso que se me ocurrió fue:
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;
}
lo que habría esperado EnumerateFiles()
a ser un poco más rápido, pero resultó un poco más lento (que podría hacerlo mejor si estás revisando una red, pero no lo probé).
Hay una ligera ganancia con:
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);
}
pero no mucho, ya que no ayuda a la llamada real a GetFiles()
. Si no tiene los núcleos para usar, o no hay un resultado lo suficientemente grande como GetFiles()
, empeorará las cosas (los gastos generales de AsParallel()
superan el beneficio de realizar el filtrado en paralelo).Por otro lado, si puede hacer sus próximos pasos de procesamiento también en paralelo, entonces la velocidad general de la aplicación podría mejorar.
Parece que no tiene sentido hacer esto con EnumerateFiles()
porque no parece paralelizarse bien, porque está basado en el mismo enfoque al que voy a llegar, y que es intrínsecamente serial - necesita el resultado previo para producir el siguiente.
El más rápido lo que obtuve fue:
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);
}
}
Es arriesgado no tener que FindClose()
prometido por un IDisposable
, y una aplicación enrollado a mano de IEnumerator<string>
no sólo debe hacer que sea más fácil hacer (razón de peso para hacerlo) pero también se espera que se reduzca como 3 nanosegundos o algo así (no es una razón seria para hacerlo), pero lo anterior muestra la idea básica.
Puede intentar usar LINQ. – Bernard