¿Cómo encontrar el archivo más reciente en un directorio usando .NET y sin bucles?

142

Necesito encontrar el archivo modificado más recientemente en un directorio.

Sé que puedo recorrer todos los archivos de una carpeta y comparar File.GetLastWriteTime, pero ¿hay una mejor manera de hacerlo sin hacerlo?

Chris Klepeis
fuente
18
No, no hay mejor manera de evitar bucles. Incluso usar LINQ solo oculta el bucle en una funcionalidad más profunda donde no se puede ver directamente.
Oliver
1
Si desea encontrar los archivos modificados más recientemente en todo el sistema de archivos, el Diario de cambios de NTFS sería útil. Sin embargo, es muy, muy difícil de usar desde C #.
Ben Voigt

Respuestas:

318

Qué tal algo como esto...

var directory = new DirectoryInfo("C:\\MyDirectory");
var myFile = (from f in directory.GetFiles()
             orderby f.LastWriteTime descending
             select f).First();

// or...
var myFile = directory.GetFiles()
             .OrderByDescending(f => f.LastWriteTime)
             .First();
Scott Ivey
fuente
84
Personalmente, encuentro que la versión no azucarada es más fácil de leer:directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First()
Jørn Schou-Rode
66
Sí, también estoy de acuerdo la mayor parte del tiempo, pero al dar ejemplos, la sintaxis de la consulta hace que sea un poco más obvio que es una consulta linq. Actualizaré el ejemplo con ambas opciones para aclarar.
Scott Ivey
3
¡Gracias! Ahora solo necesito convencer a mi jefe para que agilice el proceso de actualización de .net 2.0 para poder usar Linq :)
Chris Klepeis
3
puede usar linq con 2.0 SP1 con un poco de trabajo extra: solo haga referencia al archivo System.Core.dll de 3.5 y configúrelo para "copiar localmente"
Scott Ivey,
8
¿No debería este uso en FirstOrDefault()lugar de First()? Este último causará un InvalidOperationExceptionsi el directorio está vacío.
Tobias J
20

Si desea buscar de forma recursiva, puede usar este hermoso código:

public static FileInfo GetNewestFile(DirectoryInfo directory) {
   return directory.GetFiles()
       .Union(directory.GetDirectories().Select(d => GetNewestFile(d)))
       .OrderByDescending(f => (f == null ? DateTime.MinValue : f.LastWriteTime))
       .FirstOrDefault();                        
}

Solo llámalo de la siguiente manera:

FileInfo newestFile = GetNewestFile(new DirectoryInfo(@"C:\directory\"));

y eso es. Devuelve una FileInfoinstancia o nullsi el directorio está vacío.

Edgar Villegas Alvarado
fuente
12
O puede usar la opción de búsqueda recursiva .
ricksmt
17

Ampliando el primero de arriba, si desea buscar un cierto patrón, puede usar el siguiente código:

string pattern = "*.txt";
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();
Zamir
fuente
Yo necesitaba eso. Gracias.
ashilon
10

Una versión no LINQ:

/// <summary>
/// Returns latest writen file from the specified directory.
/// If the directory does not exist or doesn't contain any file, DateTime.MinValue is returned.
/// </summary>
/// <param name="directoryInfo">Path of the directory that needs to be scanned</param>
/// <returns></returns>
private static DateTime GetLatestWriteTimeFromFileInDirectory(DirectoryInfo directoryInfo)
{
    if (directoryInfo == null || !directoryInfo.Exists)
        return DateTime.MinValue;

    FileInfo[] files = directoryInfo.GetFiles();
    DateTime lastWrite = DateTime.MinValue;

    foreach (FileInfo file in files)
    {
        if (file.LastWriteTime > lastWrite)
        {
            lastWrite = file.LastWriteTime;
        }
    }

    return lastWrite;
}

/// <summary>
/// Returns file's latest writen timestamp from the specified directory.
/// If the directory does not exist or doesn't contain any file, null is returned.
/// </summary>
/// <param name="directoryInfo">Path of the directory that needs to be scanned</param>
/// <returns></returns>
private static FileInfo GetLatestWritenFileFileInDirectory(DirectoryInfo directoryInfo)
{
    if (directoryInfo == null || !directoryInfo.Exists)
        return null;

    FileInfo[] files = directoryInfo.GetFiles();
    DateTime lastWrite = DateTime.MinValue;
    FileInfo lastWritenFile = null;

    foreach (FileInfo file in files)
    {
        if (file.LastWriteTime > lastWrite)
        {
            lastWrite = file.LastWriteTime;
            lastWritenFile = file;
        }
    }
    return lastWritenFile;
}
TimothyP
fuente
1
Lo sentimos, no vi el hecho de que no quisieras repetir. En fin ... tal vez ayude a alguien que busca algo parecido
TimothyP
3
Este código no se compila. - lastUpdatedFile no debe ser una matriz. - El valor inicial de lastUpdate no es válido (0001/0/0).
Lars A. Brekken
4

Corto y simple :

new DirectoryInfo(path).GetFiles().OrderByDescending(o => o.LastWriteTime).FirstOrDefault();
Jacob
fuente
3

es un poco tarde pero ...

su código no funcionará debido a list<FileInfo> lastUpdateFile = null; y más tarde, lastUpdatedFile.Add(file);por lo que se generará una excepción NullReference. La versión de trabajo debe ser:

private List<FileInfo> GetLastUpdatedFileInDirectory(DirectoryInfo directoryInfo)
{
    FileInfo[] files = directoryInfo.GetFiles();
    List<FileInfo> lastUpdatedFile = new List<FileInfo>();
    DateTime lastUpdate = DateTime.MinValue;
    foreach (FileInfo file in files)
    {
        if (file.LastAccessTime > lastUpdate)
        {
            lastUpdatedFile.Add(file);
            lastUpdate = file.LastAccessTime;
        }
    }

    return lastUpdatedFile;
}

Gracias

Oleg Karbushev
fuente
2

Puede reaccionar a la nueva actividad de archivo con FileSystemWatcher .

Scott Marlowe
fuente
1
No funciona porque un archivo puede modificarse mientras su aplicación no se está ejecutando.
Francis B.
1
no dio ese tipo de detalles ... ¿Cómo sabemos que no es una aplicación persistente?
Scott Marlowe
1
No lo hacemos, pero Scott tiene una mejor solución que funciona en ambos casos.
Badaro
Además, tenga en cuenta que FSW no funcionará con la mayoría de las carpetas compartidas de red.
bkqc
2

Otro enfoque si está utilizando Directory.EnumerateFilesy desea leer los archivos modificados por primera vez.

foreach (string file in Directory.EnumerateFiles(fileDirectory, fileType).OrderByDescending(f => new FileInfo(f).LastWriteTime))

}
Gaurravs
fuente
1

Aquí hay una versión que obtiene el archivo más reciente de cada subdirectorio

List<string> reports = new List<string>();    
DirectoryInfo directory = new DirectoryInfo(ReportsRoot);
directory.GetFiles("*.xlsx", SearchOption.AllDirectories).GroupBy(fl => fl.DirectoryName)
.ForEach(g => reports.Add(g.OrderByDescending(fi => fi.LastWriteTime).First().FullName));
Michael Bahig
fuente
0
private List<FileInfo> GetLastUpdatedFileInDirectory(DirectoryInfo directoryInfo)
{
    FileInfo[] files = directoryInfo.GetFiles();
    List<FileInfo> lastUpdatedFile = null;
    DateTime lastUpdate = new DateTime(1, 0, 0);
    foreach (FileInfo file in files)
    {
        if (file.LastAccessTime > lastUpdate)
        {
            lastUpdatedFile.Add(file);
            lastUpdate = file.LastAccessTime;
        }
    }

    return lastUpdatedFile;
}
Sylver1981
fuente
0

Hago esto es un montón de mis aplicaciones y uso una declaración como esta:

  var inputDirectory = new DirectoryInfo("\\Directory_Path_here");
  var myFile = inputDirectory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();

Desde aquí tendrá el nombre del archivo guardado / agregado / actualizado más recientemente en el directorio de la variable "inputDirectory". Ahora puede acceder a él y hacer lo que quiera con él.

Espero que ayude.

JasonR
fuente
Para agregar a esto, si su objetivo es recuperar la ruta del archivo, y está usando FirstOrDefault porque es posible que no haya resultados, puede usar el operador condicional nulo en la propiedad FullName. Esto lo devolverá "nulo" para su ruta sin tener que guardar el FileInfo de FirstOrDefault antes de llamar a FullName. string path = new DirectoryInfo (ruta) .GetFiles (). OrderByDescending (o => o.LastWriteTime) .FirstOrDefault ()? FullName;
StalePhish