Tengo un directorio con alrededor de 15-30 mil archivos. Necesito simplemente sacar el más viejo. En otras palabras, el que fue creado primero. ¿Hay una manera rápida de hacer esto usando C#, aparte de cargarlos en una colección y luego ordenarlos?cómo obtener el archivo más antiguo en un directorio rápido con .NET?
Respuesta
La respuesta corta es no. Los sistemas de archivos de Windows no indexan los archivos por fecha, por lo que no existe una forma nativa de hacerlo, y mucho menos un camino .net sin enumerarlos todos.
¿Qué pasa con el objeto FileSystemInfo? – andrecarlucci
La clasificación es O(n log n)
. En cambio, ¿por qué no solo enumera el directorio? No estoy seguro de lo que el equivalente C# de FindFirstFile()
/FindNextFile()
es, pero quiero hacer es:
mantener actualizada la fecha y el nombre más bajo en una variable local.
Enumerar el directorio.
- Si la fecha de un archivo determinado es menor que la variable local, establezca la variable local en la nueva fecha y nombre de archivo.
Realmente no hay FindFirst/Next. Está envuelto en Directory.GetFiles. Pero tienes razón en que lo único que necesitas es una búsqueda lineal, que es directa O (n). –
En ese caso, podría incluso preguntarme si podría reducir algunos ciclos pinvoking las API de enumeración nativas ... No hay necesidad de hacer una asignación de memoria adicional aquí para obtener una gran variedad. Aún así, no obtendrás mejores resultados que 'O (n)' (en ese sentido, la sugerencia DB es mejor), pero puedes obtener 'O (n)' con un factor constante menor y eso podría ser suficiente. – asveikau
Además, las API de enumeración nativa le dan la hora y el nombre del archivo al mismo tiempo; no parece que 'GetFiles()' lo haga. – asveikau
Si controlas el directorio (es decir, si sus programas son responsables de la creación y el mantenimiento de todos los archivos de ese directorio), entonces debería considerar el seguimiento de los metadatos acerca de cada archivo por separado; quizás en una base de datos.
De hecho, el tipo de columna FileStream en SQL Server 2008 puede ayudar con esto. Puede crear una tabla que contenga columnas para nombre de archivo, crear fecha, modificar fecha y una columna FileStream para el contenido. Puede encontrar cosas como el archivo más antiguo usando índices en las columnas de metadatos. Puede encontrar el contenido utilizando la columna FileStream.
Esto no está mal, pero bien puede ser excesivo. –
@Steven: Teniendo en cuenta la cantidad de archivos, tal vez no.También me pregunto qué otras operaciones de base de datos se realizan en tales directorios. Todo es discutible si él no controla los directorios involucrados. –
Me pregunto cuál es el panorama general aquí. –
Mira, ¿no sería más fácil de pagar a un proceso oculto y redirigir el flujo de salida a la entrada y el uso de la dir /o-d
que clasifica por la fecha/hora, usando el tablero invierte la operación ....
Editar: aquí hay un código de ejemplo para hacer esto ... rápido y sucio ...
public class TestDir { private StringBuilder sbRedirectedOutput = new StringBuilder(); public string OutputData { get { return this.sbRedirectedOutput.ToString(); } } public void Run() { System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo(); ps.FileName = "cmd"; ps.ErrorDialog = false; ps.Arguments = string.Format("dir {0} /o-d", path_name); ps.CreateNoWindow = true; ps.UseShellExecute = false; ps.RedirectStandardOutput = true; ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; using (System.Diagnostics.Process proc = new System.Diagnostics.Process()) { proc.StartInfo = ps; proc.Exited += new EventHandler(proc_Exited); proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived); proc.Start(); proc.WaitForExit(); proc.BeginOutputReadLine(); while (!proc.HasExited) ; } } void proc_Exited(object sender, EventArgs e) { System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended"); } void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) { if (e.Data != null) this.sbRedirectedOutput.Append(e.Data + Environment.NewLine); //System.Diagnostics.Debug.WriteLine("proc_OutputDataReceived: Data: " + e.Data); } }
los primeros 4 o 5 líneas del objeto StringBuilder sbRedirectedOutput
se pueden cortar a cabo, a continuación, después de que la línea contendría el nombre de archivo más antiguo y sería bastante fácil de analizar ...
No veo el beneficio de esto. –
@Steven: la lista del directorio podría redirigirse a una cadena, y es cuestión de elegir el nombre del archivo en la parte superior de la lista ... – t0mm13b
"Mira, ¿no sería más fácil ..." Ummm ... no . –
Editar: Eliminó el género y lo convirtió en una función.
public static FileInfo GetOldestFile(string directory)
{
if (!Directory.Exists(directory))
throw new ArgumentException();
DirectoryInfo parent = new DirectoryInfo(directory);
FileInfo[] children = parent.GetFiles();
if (children.Length == 0)
return null;
FileInfo oldest = children[0];
foreach (var child in children.Skip(1))
{
if (child.CreationTime < oldest.CreationTime)
oldest = child;
}
return oldest;
}
Aquí está una rutina de C# que puede hacer lo que quiera por el desove de una consola CMD ejecutar un dir /o:D
en el directorio especificado y devolver el nombre del primer archivo encontrado.
static string GetOldestFile(string dirName)
{
ProcessStartInfo si = new ProcessStartInfo("cmd.exe");
si.RedirectStandardInput = true;
si.RedirectStandardOutput = true;
si.UseShellExecute = false;
Process p = Process.Start(si);
p.StandardInput.WriteLine(@"dir " + dirName + " /o:D");
p.StandardInput.WriteLine(@"exit");
string output = p.StandardOutput.ReadToEnd();
string[] splitters = { Environment.NewLine };
string[] lines = output.Split(splitters, StringSplitOptions.RemoveEmptyEntries);
// find first line with a valid date that does not have a <DIR> in it
DateTime result;
int i = 0;
while (i < lines.Length)
{
string[] tokens = lines[i].Split(' ');
if (DateTime.TryParse(tokens[0], out result))
{
if (!lines[i].Contains("<DIR>"))
{
return tokens[tokens.Length - 1];
}
}
i++;
}
return "";
}
Estaba pensando en seguir esta ruta, pero es casi lo mismo que un enfoque LINQ ¿verdad? –
Aunque esta solución es correcta, es lenta y está llena de casos de esquina. El comprador tenga cuidado. – rpetrich
Usted tendrá que cargar los objetos de FileInfo en una especie recogida &, pero es una sola línea:
FileSystemInfo fileInfo = new DirectoryInfo(directoryPath).GetFileSystemInfos()
.OrderByDescending(fi => fi.CreationTime).First();
Ok, dos líneas porque es una larga declaración.
Gran respuesta simple. No será mejor que esto sin un cambio en la estructura del directorio IMO. –
Nunca ordene una colección si su objetivo es solo devolver un solo artículo. Use Agregado() en su lugar. Para ver un ejemplo, consulte: http://stackoverflow.com/questions/10120944/c-sharp-finding-nearest-number-in-array/13896732#13896732 –
@LeeGrissom gracias por el enlace; Realmente no había considerado ninguna sobrecarga antes, ya que asumí que el 'OrderBy' era flojo y no se evaluaría del todo, pero no se había probado ni comprobado. –
No puede hacerlo sin ordenar, pero lo que puede hacer es hacerlo rápido.
La ordenación por CreationTime
puede ser lenta porque el primer acceso a esta propiedad para cada archivo implica la interrogación del sistema de archivos.
Use A Faster Directory Enumerator que conserva más información sobre los archivos al enumerar y permite hacer la ordenación más rápido.
Código de comparar el rendimiento:
static void Main(string[] args)
{
var timer = Stopwatch.StartNew();
var oldestFile = FastDirectoryEnumerator.EnumerateFiles(@"c:\windows\system32")
.OrderBy(f => f.CreationTime).First();
timer.Stop();
Console.WriteLine(oldestFile);
Console.WriteLine("FastDirectoryEnumerator - {0}ms", timer.ElapsedMilliseconds);
Console.WriteLine();
timer.Reset();
timer.Start();
var oldestFile2 = new DirectoryInfo(@"c:\windows\system32").GetFiles()
.OrderBy(f => f.CreationTime).First();
timer.Stop();
Console.WriteLine(oldestFile2);
Console.WriteLine("DirectoryInfo - {0}ms", timer.ElapsedMilliseconds);
Console.WriteLine("Press ENTER to finish");
Console.ReadLine();
}
Para mí esto da:
VEN2232.OLB
FastDirectoryEnumerator - 27 ms
VEN2232.OLB
DirectoryInfo - 559m s
Todo lo que puedo decir es ¡WoW - ShamWoW! Muchas gracias por esto, ciertamente implementaré esto. –
@ Konstantin: en .NET 4.0, el método 'DirectoryInfo.GetFiles()' ahora conserva la información adicional, por lo que no sufre el problema de rendimiento al que se ha dirigido. –
Por extraño que parezca, esto funcionó perfectamente en un directorio de la mía con 3000 + archivos jpg:
DirectoryInfo di = new DirectoryInfo(dpath);
FileInfo[] rgFiles = di.GetFiles("*.jpg");
FileInfo firstfile = rgFiles[0];
FileInfo lastfile = rgFiles[rgFiles.Length - 1];
DateTime oldestfiletime = firstfile.CreationTime;
DateTime newestfiletime = lastfile.CreationTime;
- 1. Encontrar el archivo más antiguo (recursivamente) en un directorio
- 2. Buscar el archivo más antiguo en una carpeta usando PHP
- 3. Cómo obtener el archivo más nuevo en un directorio en php
- 4. ¿Cómo encontrar el archivo más reciente en un directorio usando .NET y sin bucles?
- 5. Python: ¿Cómo obtener archivos en un archivo sin el directorio?
- 6. ¿Cómo puedo comprimir un directorio con .NET?
- 7. ¿Cómo obtener el directorio de un archivo argparse en Python?
- 8. Análisis más rápido de números en .NET
- 9. IOS - Obtener suma de tamaño de archivo en el directorio
- 10. Obtener cada archivo en un directorio, Python
- 11. ¿Cómo encontrar el archivo en cada directorio con el número más alto como nombre de archivo?
- 12. Java: obtenga el archivo más nuevo en un directorio?
- 13. RSpec más rápido con JRuby
- 14. Obtener el directorio de un archivo de script bash en
- 15. Cómo hacer que mi aplicación copie el archivo más rápido
- 16. ¿Cómo obtener el directorio actual?
- 17. .NET: serializar el objeto a un archivo de un ensamblado de un tercero (para hacer más rápido el Selenium WebDriver)
- 18. Método más rápido para invertir en cadena en C# .net
- 19. ¿Es el archivo paralelo.Leer más rápido que la lectura secuencial?
- 20. Cómo obtener el directorio más reciente (última modificación) [C#]
- 21. ¿Cómo puedo obtener JQueryUI ordenable para 'revertir' más rápido?
- 22. Cómo obtener la AssemblyVersion de un archivo .Net en Linux
- 23. ¿Cómo eliminar más rápido?
- 24. Script Bash para scp el archivo más nuevo en un directorio en un servidor remoto
- 25. ¿Se actualizó el código antiguo de .NET en nuevas versiones?
- 26. .NET filesystemwatcher - ¿era un archivo o un directorio?
- 27. Usando .NET 2.0, ¿cómo puedo FTP a un servidor, obtener un archivo y eliminar el archivo?
- 28. ¿Hay algo más rápido que SqlDataReader en .NET?
- 29. Comparar el archivo tar del directorio con el directorio original
- 30. Obtener todos los nombres de archivo en un directorio con el rubí
Yo tenía entendido que tener un gran número de archivos en un directorio reduce el rendimiento. Piensa que has pasado ese punto. –
Uhm, lo que dijo John. Agregue algunas barras para dividirlo en subdirectorios. –
A menos que necesite hacer eso varias veces por segundo, 15-30k no son tantos archivos. – Joey