Як знайти найсвіжіший файл у каталозі за допомогою .NET і без циклу?


142

Мені потрібно знайти самий нещодавно змінений файл у каталозі.

Я знаю, що можу переглядати кожен файл у папці та порівнювати File.GetLastWriteTime, але чи є кращий спосіб зробити це без циклу ?.


18
Ні, немає кращого способу, який дозволяє уникнути циклу. Навіть використовуючи LINQ, просто приховує цикл в деякій більш глибокій функціональності, де ви не можете бачити його безпосередньо.
Олівер

1
Якщо ви хочете знайти останні файли (файли) у всій файловій системі, тоді журнал змін NTFS був би корисним. Однак дуже важко використовувати C #.
Ben Voigt

Відповіді:


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()
Jørn Schou-Rode

6
так, я згоден і більшу частину часу, але, коли наводити приклади, синтаксис запитів робить його трохи очевиднішим, що це запит linq. Я оновлю приклад з обома параметрами для уточнення.
Скотт Айві

3
Дякую! Тепер мені просто потрібно переконати свого боса прискорити процес модернізації нас з .net 2.0, щоб я міг скористатися Linq :)
Кріс Клепейс,

3
ви можете використовувати linq з 2.0 SP1 з невеликою додатковою роботою - просто посилайтесь на файл System.Core.dll з 3.5, і встановіть його на "копіювати локально"
Скотт Айві

8
Чи не слід це використовувати FirstOrDefault()замість First()? Останнє спричинить, InvalidOperationExceptionякщо каталог порожній.
Тобіас J

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
Або ви можете скористатися параметром рекурсивного пошуку .
ricksmt

17

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

string pattern = "*.txt";
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();

Мені це було потрібно. Дякую.
асилон

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

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
Ми цього не робимо, але у Скотта краще рішення, що працює в обох випадках.
Бадаро

Також зауважте, що FSW не працюватиме з більшістю загальнодоступних папок у мережі.
bkqc

2

Інший підхід, якщо ви використовуєте Directory.EnumerateFilesі хочете прочитати файли останніми зміненими спочатку.

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

}

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

0

Я роблю це купу моїх додатків, і я використовую такий вислів:

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

Звідси ви отримаєте ім'я файлу для останнього збереженого / доданого / оновленого файла в Каталозі змінної "inputDirectory". Тепер ви можете отримати до нього доступ і робити все, що завгодно.

Сподіваюся, що це допомагає.


Щоб додати до цього, якщо ваша мета - повернути шлях до файлу, а ви використовуєте FirstOrDefault, оскільки можливо, що результатів немає, ви можете скористатися оператором null-conditional у властивості FullName. Це поверне вас назад "null" для вашого шляху, не потребуючи збереження FileInfo від FirstOrDefault перед тим, як викликати FullName. рядок шлях = новий DirectoryInfo (шлях) .GetFiles (). OrderByDescending (o => o.LastWriteTime) .FirstOrDefault () ?. FullName;
StalePhish
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.