Como encontrar o arquivo mais recente em um diretório usando .NET e sem loop?

142

Preciso encontrar o arquivo modificado mais recentemente em um diretório.

Eu sei que posso percorrer todos os arquivos em uma pasta e comparar File.GetLastWriteTime, mas existe uma maneira melhor de fazer isso sem fazer um loop?

Chris Klepeis
fonte
18
Não, não existe melhor maneira de evitar o loop. Mesmo o uso do LINQ oculta o loop em algumas funcionalidades mais profundas, nas quais você não pode vê-lo diretamente.
Oliver
1
Se você deseja encontrar os arquivos modificados mais recentemente em todo o sistema de arquivos, o Diário de alterações do NTFS será útil. Muito, muito difícil de usar a partir de C #.
Ben Voigt 27/05

Respostas:

318

Que tal algo como isso...

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
fonte
84
Pessoalmente, acho que a versão não-açucarada é mais fácil de ler:directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First()
Jørn Schou-Rode
6
Sim, eu concordo na maioria das vezes também - mas, ao dar exemplos, a sintaxe da consulta torna um pouco mais óbvio que é uma consulta linq. Vou atualizar o exemplo com as duas opções para esclarecer.
23911 Scott Ivey
3
Obrigado! Agora eu só preciso convencer meu chefe para agilizar o processo de modernização us a partir .NET 2.0 para que eu possa usar Linq :)
Chris Klepeis
3
você pode usar LINQ com 2.0 SP1 com um pouco de trabalho extra - basta fazer referência ao arquivo System.Core.dll de 3,5, e configurá-lo para "copiar local"
Scott Ivey
8
Isso não deveria ser usado em FirstOrDefault()vez de First()? Este último causará um InvalidOperationExceptionse o diretório estiver vazio.
Tobias J
20

Se você deseja pesquisar recursivamente, pode usar este belo pedaço de 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();                        
}

Basta chamá-lo da seguinte maneira:

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

e é isso. Retorna uma FileInfoinstância ou nullse o diretório está vazio.

Edgar Villegas Alvarado
fonte
12
Ou você pode usar a opção de pesquisa recursiva .
Rickmt #
17

Expandindo o primeiro acima, se você deseja procurar um determinado padrão, pode usar o seguinte 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
fonte
Eu precisava disso. Obrigado.
ashilon
10

Uma versão não-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
fonte
1
Desculpe, você não viu o fato de que não queria fazer um loop. Enfim ... talvez ele vai ajudar alguém procurar algo semelhante
TimothyP
3
Este código não compila. - lastUpdatedFile não deve ser uma matriz. - O valor inicial para lastUpdate é inválido (0001/0/0).
Lars A. Brekken
4

Curto e simples :

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

é um pouco tarde, mas ...

seu código não funcionará, por causa list<FileInfo> lastUpdateFile = null; e mais tarde, lastUpdatedFile.Add(file);então a exceção NullReference será lançada. A versão de trabalho deve 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;
}

obrigado

Oleg Karbushev
fonte
2

Você pode reagir à nova atividade de arquivo com o FileSystemWatcher .

Scott Marlowe
fonte
1
Não funciona porque um arquivo pode ser modificado enquanto o aplicativo não está sendo executado.
Francis B.
1
ele não deu esse tipo de detalhe ... Como sabemos que não é um aplicativo persistente?
23411 Scott Marlowe
1
Não temos, mas Scott tem uma solução melhor que funciona nos dois casos.
243 Badaro
Além disso, observe que o FSW não funcionará com a maioria das pastas compartilhadas da rede.
bkqc
2

Outra abordagem, se você estiver usando Directory.EnumerateFilese quiser ler os arquivos modificados pela última vez primeiro.

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

}
Gaurravs
fonte
1

Aqui está uma versão que obtém o arquivo mais recente de cada subdiretório

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
fonte
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
fonte
0

Faço isso em vários aplicativos e uso uma declaração como esta:

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

A partir daqui, você terá o nome do arquivo para o arquivo salvo / adicionado / atualizado mais recentemente no diretório da variável "inputDirectory". Agora você pode acessá-lo e fazer o que quiser com ele.

Espero que ajude.

JasonR
fonte
Para adicionar isso, se seu objetivo é recuperar o caminho do arquivo e você estiver usando FirstOrDefault porque é possível que não haja resultados, você pode usar o operador condicional nulo na propriedade FullName. Isso retornará "nulo" para o seu caminho sem precisar salvar o FileInfo do FirstOrDefault antes de chamar FullName. caminho da cadeia de caracteres = new DirectoryInfo (caminho). GetFiles (). OrderByDescending (o => o.LastWriteTime) .FirstOrDefault () ?. FullName;
StalePhish