Как найти самый последний файл в каталоге с использованием .NET и без зацикливания?

142

Мне нужно найти самый последний измененный файл в каталоге.

Я знаю, что могу просмотреть все файлы в папке и сравнить их File.GetLastWriteTime, но есть ли лучший способ сделать это без циклов?

Крис Клепейс
источник
18
Нет, лучшего способа избежать петли нет. Даже использование LINQ просто скрывает зацикливание в некоторой более глубокой функциональности, где вы не можете видеть это непосредственно.
Оливер
1
Если вы хотите найти самые последние измененные файлы во всей файловой системе, тогда журнал изменений NTFS будет полезен. Очень, очень трудно использовать из C #, хотя.
Бен Фойгт

Ответы:

318

как насчет чего-то вроде этого ...

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();
Скотт Айви
источник
84
Лично я считаю, что несахаренную версию легче читать:directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First()
Йорн Шоу-Роде
6
да, я тоже согласен большую часть времени - но когда приводим примеры, синтаксис запроса делает его более очевидным, что это запрос linq. Я уточню пример с обоими вариантами, чтобы уточнить.
Скотт Айви
3
Спасибо! Теперь мне просто нужно убедить моего босса ускорить процесс обновления нас с .net 2.0, чтобы я мог использовать Linq :)
Крис Клепейс,
3
Вы можете использовать linq с 2.0 SP1 с небольшой дополнительной работой - просто обратитесь к файлу System.Core.dll из 3.5 и установите для него значение «копировать локально»
Скотт Айви,
8
Разве это не должно использовать FirstOrDefault()вместо First()? Последний вызовет, InvalidOperationExceptionесли каталог пуст.
Тобиас Дж
20

Если вы хотите выполнять рекурсивный поиск, вы можете использовать этот прекрасный фрагмент кода:

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

Просто назовите это следующим образом:

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

и это все. Возвращает FileInfoэкземпляр или, nullесли каталог пуст.

Эдгар Виллегас Альварадо
источник
12
Или вы можете использовать опцию рекурсивного поиска .
Риксм
17

Развернув первый вариант выше, если вы хотите выполнить поиск определенного шаблона, вы можете использовать следующий код:

string pattern = "*.txt";
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();
Замир
источник
Мне это было нужно. Спасибо.
ashilon
10

Версия без 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
источник
1
Извините, не увидел тот факт, что вы не хотели зацикливаться. Во всяком случае ... возможно, это поможет кому-то искать что-то подобное
TimothyP
3
Этот код не компилируется. - lastUpdatedFile не должен быть массивом. - Начальное значение для lastUpdate недействительно (0001/0/0).
Ларс А. Бреккен
4

Коротко и просто :

new DirectoryInfo(path).GetFiles().OrderByDescending(o => o.LastWriteTime).FirstOrDefault();
Иаков
источник
3

уже немного поздно, но ...

ваш код не будет работать, потому что list<FileInfo> lastUpdateFile = null; и позже, lastUpdatedFile.Add(file);так что будет выдано исключение NullReference. Рабочая версия должна быть:

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

Спасибо

Олег Карбушев
источник
2

Вы можете реагировать на новые действия с файлами с помощью FileSystemWatcher .

Скотт Марлоу
источник
1
Это не работает, потому что файл может быть изменен, когда его приложение не запущено.
Фрэнсис Б.
1
он не дал таких подробностей ... Откуда мы знаем, что это не постоянное приложение?
Скотт Марлоу
1
У нас нет, но у Скотта есть лучшее решение, которое работает в обоих случаях.
Badaro
Также обратите внимание, что FSW не будет работать с большинством общих сетевых папок.
BKQC
2

Другой подход, если вы используете Directory.EnumerateFilesи хотите читать файлы в последних модифицированных в первую очередь.

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

}
Gaurravs
источник
1

Вот версия, которая получает самый последний файл из каждого подкаталога

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));
Майкл Бахиг
источник
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
источник
0

Я делаю это несколько моих приложений, и я использую заявление как это:

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

Отсюда у вас будет имя последнего сохраненного / добавленного / обновленного файла в каталоге с переменной inputDirectory. Теперь вы можете получить к нему доступ и делать с ним все, что хотите.

Надеюсь, это поможет.

JasonR
источник
В дополнение к этому, если ваша цель - вернуть путь к файлу, и вы используете FirstOrDefault, поскольку, возможно, результатов нет, вы можете использовать оператор null-conditional в свойстве FullName. Это вернет вам «null» для вашего пути без необходимости сохранять FileInfo из FirstOrDefault перед вызовом FullName. string path = new DirectoryInfo (path) .GetFiles (). OrderByDescending (o => o.LastWriteTime) .FirstOrDefault () ?. FullName;
StalePhish