Jak znaleźć najnowszy plik w katalogu za pomocą .NET i bez zapętlania?


142

Muszę znaleźć ostatnio zmodyfikowany plik w katalogu.

Wiem, że mogę przeglądać każdy plik w folderze i porównywać File.GetLastWriteTime, ale czy jest lepszy sposób na zrobienie tego bez zapętlania?


18
Nie, nie ma lepszego sposobu na uniknięcie zapętlenia. Nawet użycie LINQ po prostu ukrywa pętlę w jakiejś głębszej funkcjonalności, w której nie można jej zobaczyć bezpośrednio.
Oliver

1
Jeśli chcesz znaleźć ostatnio zmodyfikowane pliki w całym systemie plików, przydatny byłby dziennik zmian NTFS. Jednak bardzo trudny w użyciu w C #.
Ben Voigt,

Odpowiedzi:


318

co powiesz na coś takiego ...

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
Osobiście uważam, że wersja bez cukru jest łatwiejsza do odczytania:directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First()
Jørn Schou-Rode

6
tak, w większości przypadków też się z tym zgadzam - ale podając przykłady składnia zapytania sprawia, że ​​jest to nieco bardziej oczywiste, że jest to zapytanie linq. Zaktualizuję przykład z obiema opcjami, aby wyjaśnić.
Scott Ivey

3
Dzięki! Teraz muszę tylko przekonać szefa, aby przyspieszył proces aktualizacji z .net 2.0, abym mógł używać Linq :)
Chris Klepeis

3
możesz używać linq z 2.0 SP1 z odrobiną dodatkowej pracy - po prostu odwołaj się do pliku System.Core.dll z 3.5 i ustaw go na „kopiuj lokalnie”
Scott Ivey

8
Czy nie powinno to używać FirstOrDefault()zamiast First()? Ta ostatnia spowoduje InvalidOperationException, że katalog jest pusty.
Tobias J

20

Jeśli chcesz wyszukiwać rekurencyjnie, możesz użyć tego pięknego fragmentu kodu:

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

Po prostu nazwij to w następujący sposób:

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

i to wszystko. Zwraca FileInfoinstancję lub nulljeśli katalog jest pusty.


12
Możesz też skorzystać z opcji wyszukiwania rekurencyjnego .
ricksmt

17

Rozwijając pierwszy z powyższych, jeśli chcesz wyszukać określony wzorzec, możesz użyć następującego kodu:

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

Potrzebowałem tego. Dziękuję Ci.
Ashilon

10

Wersja inna niż 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
Przepraszamy, nie widziałem faktu, że nie chcesz zapętlić. W każdym razie ... może to pomoże komuś w szukaniu czegoś podobnego
TimothyP

3
Ten kod nie kompiluje się. - lastUpdatedFile nie powinien być tablicą. - Początkowa wartość lastUpdate jest nieprawidłowa (0001/0/0).
Lars A. Brekken

4

Krótko i prosto :

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

3

jest trochę późno, ale ...

Twój kod nie będzie działał, ponieważ list<FileInfo> lastUpdateFile = null; i później lastUpdatedFile.Add(file);zostanie zgłoszony wyjątek NullReference. Wersja robocza powinna być:

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

Dzięki


2

Możesz reagować na nową aktywność plików za pomocą FileSystemWatcher .


1
To nie działa, ponieważ plik można modyfikować, gdy jego aplikacja nie jest uruchomiona.
Francis B.

1
nie podał takich szczegółów ... Skąd wiemy, że to nie jest trwała aplikacja?
Scott Marlowe

1
Nie mamy, ale Scott ma lepsze rozwiązanie, które działa w obu przypadkach.
Badaro,

Należy również pamiętać, że FSW nie będzie działać z większością udostępnionych folderów sieciowych.
bkqc

2

Inne podejście, jeśli używasz Directory.EnumerateFilesi chcesz czytać pliki w najnowszej modyfikacji przez pierwsze.

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

}

1

Oto wersja, która pobiera najnowszy plik z każdego podkatalogu

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

Robię to z kilku moich aplikacji i używam następującego oświadczenia:

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

Stąd będziesz mieć nazwę pliku dla ostatnio zapisanego / dodanego / zaktualizowanego pliku w katalogu zmiennej "inputDirectory". Teraz możesz uzyskać do niego dostęp i robić z nim, co chcesz.

Mam nadzieję, że to pomoże.


Aby to dodać, jeśli Twoim celem jest odzyskanie ścieżki pliku i używasz FirstOrDefault, ponieważ możliwe jest, że nie ma żadnych wyników, możesz użyć operatora warunkowego zerowego we właściwości FullName. Spowoduje to zwrócenie „null” dla twojej ścieżki bez konieczności zapisywania FileInfo z FirstOrDefault przed wywołaniem FullName. string path = new DirectoryInfo (path) .GetFiles (). OrderByDescending (o => o.LastWriteTime) .FirstOrDefault () ?. FullName;
StalePhish
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.