Comment trouver le fichier le plus récent dans un répertoire en utilisant .NET, et sans boucle?

142

J'ai besoin de trouver le fichier le plus récemment modifié dans un répertoire.

Je sais que je peux parcourir chaque fichier dans un dossier et comparer File.GetLastWriteTime, mais y a-t-il une meilleure façon de le faire sans boucle ?.

Chris Klepeis
la source
18
Non, il n'y a pas de meilleur moyen pour éviter les boucles. Même l'utilisation de LINQ masque simplement la boucle dans des fonctionnalités plus profondes où vous ne pouvez pas la voir directement.
Oliver
1
Si vous vouliez trouver le ou les fichiers les plus récemment modifiés sur l'ensemble du système de fichiers, le journal des modifications NTFS serait utile. Très très difficile à utiliser à partir de C #, cependant.
Ben Voigt

Réponses:

318

Que diriez-vous quelque chose comme ça...

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
la source
84
Personnellement, je trouve que la version non sucrée est plus facile à lire:directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First()
Jørn Schou-Rode
6
oui, je suis d'accord la plupart du temps aussi - mais quand on donne des exemples, la syntaxe de la requête rend un peu plus évident qu'il s'agit d'une requête linq. Je vais mettre à jour l'exemple avec les deux options pour clarifier.
Scott Ivey
3
Merci! Maintenant, j'ai juste besoin de convaincre mon patron d'accélérer le processus de mise à niveau de .net 2.0 afin que je puisse utiliser Linq :)
Chris Klepeis
3
vous pouvez utiliser linq avec 2.0 SP1 avec un peu de travail supplémentaire - il suffit de référencer le fichier System.Core.dll de 3.5 et de le définir sur "copier local"
Scott Ivey
8
Ne devrait-il pas utiliser à la FirstOrDefault()place de First()? Ce dernier provoquera un InvalidOperationExceptionsi le répertoire est vide.
Tobias J
20

Si vous souhaitez effectuer une recherche récursive, vous pouvez utiliser ce magnifique morceau de code:

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();                        
}

Appelez-le simplement de la manière suivante:

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

et c'est tout. Renvoie une FileInfoinstance ou nullsi le répertoire est vide.

Edgar Villegas Alvarado
la source
12
Ou vous pouvez utiliser l' option de recherche récursive .
ricksmt
17

En développant le premier ci-dessus, si vous souhaitez rechercher un certain modèle, vous pouvez utiliser le code suivant:

string pattern = "*.txt";
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();
Zamir
la source
J'avais besoin de ça. Je vous remercie.
ashilon
10

Une version non 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
la source
1
Désolé, je n'ai pas vu le fait que vous ne vouliez pas faire de boucle. Quoi qu'il en soit ... peut-être que cela aidera quelqu'un à chercher quelque chose de similaire
TimothyP
3
Ce code ne compile pas. - lastUpdatedFile ne doit pas être un tableau. - La valeur initiale de lastUpdate n'est pas valide (0001/0/0).
Lars A. Brekken
4

Bref et simple :

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

c'est un peu tard mais ...

votre code ne fonctionnera pas, à cause de list<FileInfo> lastUpdateFile = null; et plus tard lastUpdatedFile.Add(file);, une exception NullReference sera donc levée. La version de travail doit être:

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;
}

Merci

Oleg Karbushev
la source
2

Vous pouvez réagir à une nouvelle activité de fichier avec FileSystemWatcher .

Scott Marlowe
la source
1
Cela ne fonctionne pas car un fichier peut être modifié alors que son application n'est pas en cours d'exécution.
Francis B.
1
il n'a pas donné ce genre de détails ... Comment savons-nous que ce n'est pas une application persistante?
Scott Marlowe
1
Non, mais Scott a une meilleure solution qui fonctionne dans les deux cas.
Badaro
Notez également que FSW ne fonctionnera pas avec la plupart des dossiers partagés en réseau.
bkqc
2

Une autre approche si vous utilisez Directory.EnumerateFileset souhaitez lire les fichiers dans la dernière modification par en premier.

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

}
Gaurravs
la source
1

Voici une version qui récupère le fichier le plus récent de chaque sous-répertoire

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
la source
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
la source
0

Je fais ceci est un tas de mes applications et j'utilise une déclaration comme celle-ci:

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

De là, vous aurez le nom du fichier le plus récemment enregistré / ajouté / mis à jour dans le répertoire de la variable "inputDirectory". Vous pouvez maintenant y accéder et en faire ce que vous voulez.

J'espère que cela pourra aider.

JasonR
la source
Pour ajouter à cela, si votre objectif est de récupérer le chemin du fichier et que vous utilisez FirstOrDefault car il est possible qu'il n'y ait aucun résultat, vous pouvez utiliser l'opérateur conditionnel null sur la propriété FullName. Cela vous retournera "null" pour votre chemin sans avoir à enregistrer le FileInfo de FirstOrDefault avant d'appeler FullName. string path = new DirectoryInfo (chemin) .GetFiles (). OrderByDescending (o => o.LastWriteTime) .FirstOrDefault () ?. FullName;
StalePhish