как получить самый старый файл в каталоге быстро с помощью.NET?

У меня есть каталог приблизительно с 15-30 тысячами файлов. Я должен просто вытянуть самый старый. Другими словами, тот, который был создан сначала. Существует ли быстрый способ сделать это использование C# кроме загрузки их в набор, тогда сортирующий?

11
задан John Saunders 8 March 2010 в 05:07
поделиться

9 ответов

Короткий ответ - нет. Файловые системы Windows не индексируют файлы по дате, поэтому не существует собственного способа сделать это, не говоря уже о .net способе без перечисления всех файлов.

7
ответ дан 3 December 2019 в 01:00
поделиться

Вам придется загрузить объекты FileInfo в коллекцию и отсортировать, но это делается в одну строку:

FileSystemInfo fileInfo = new DirectoryInfo(directoryPath).GetFileSystemInfos()
    .OrderBy(fi => fi.CreationTime).First();

Хорошо, в две строки, потому что это длинное утверждение.

19
ответ дан 3 December 2019 в 01:00
поделиться

Редактировать: Удалена сортировка и преобразована в функцию.

public static FileInfo GetOldestFile(string directory)
{
    if (!Directory.Exists(directory))
        throw new ArgumentException();

    DirectoryInfo parent = new DirectoryInfo(directory);
    FileInfo[] children = parent.GetFiles();
    if (children.Length == 0)
        return null;

    FileInfo oldest = children[0];
    foreach (var child in children.Skip(1))
    {
        if (child.CreationTime < oldest.CreationTime)
            oldest = child;
    }

    return oldest;
}
4
ответ дан 3 December 2019 в 01:00
поделиться

Вы не можете сделать это без сортировки, но что вы можете сделать, так это сделать ее быстрой.

Сортировка по CreationTime может быть медленной, потому что первый доступ к этому свойству для каждого файла включает опрос файловой системы.

Используйте A Faster Directory Enumerator, который сохраняет больше информации о файлах при перечислении и позволяет быстрее выполнять сортировку.

Код для сравнения производительности:

static void Main(string[] args)
{
    var timer = Stopwatch.StartNew();

    var oldestFile = FastDirectoryEnumerator.EnumerateFiles(@"c:\windows\system32")
        .OrderBy(f => f.CreationTime).First();

    timer.Stop();

    Console.WriteLine(oldestFile);
    Console.WriteLine("FastDirectoryEnumerator - {0}ms", timer.ElapsedMilliseconds);
    Console.WriteLine();

    timer.Reset();
    timer.Start();

    var oldestFile2 = new DirectoryInfo(@"c:\windows\system32").GetFiles()
        .OrderBy(f => f.CreationTime).First();

    timer.Stop();

    Console.WriteLine(oldestFile2);
    Console.WriteLine("DirectoryInfo - {0}ms", timer.ElapsedMilliseconds);

    Console.WriteLine("Press ENTER to finish");
    Console.ReadLine();
}

Для меня это дает следующее:

VEN2232.OLB

FastDirectoryEnumerator - 27ms

VEN2232.OLB

DirectoryInfo - 559ms

4
ответ дан 3 December 2019 в 01:00
поделиться

Сортировка O (n log n) . Вместо этого, почему бы вам просто не перечислить каталог? Я не уверен, что такое эквивалент C # FindFirstFile () / FindNextFile () , но вы хотите сделать следующее:

  • Сохраните текущую наименьшую дату и имя файла в локальная переменная.

  • Пронумеровать каталог.

    • Если дата в данном файле меньше, чем локальная переменная, установите для локальной переменной новую дату и имя файла.
2
ответ дан 3 December 2019 в 01:00
поделиться

Вот подпрограмма C #, которая может делать то, что вы хотите, порождая командную оболочку, запускает dir / o: D в указанном каталоге и возвращает имя первого найденного файла.

        static string GetOldestFile(string dirName)
        {
            ProcessStartInfo si = new ProcessStartInfo("cmd.exe");
            si.RedirectStandardInput = true;
            si.RedirectStandardOutput = true;
            si.UseShellExecute = false;
            Process p = Process.Start(si);
            p.StandardInput.WriteLine(@"dir " + dirName + " /o:D");
            p.StandardInput.WriteLine(@"exit");
            string output = p.StandardOutput.ReadToEnd();
            string[] splitters = { Environment.NewLine };
            string[] lines = output.Split(splitters, StringSplitOptions.RemoveEmptyEntries);
            // find first line with a valid date that does not have a <DIR> in it
            DateTime result;
            int i = 0;
            while (i < lines.Length)
            {
                string[] tokens = lines[i].Split(' ');
                if (DateTime.TryParse(tokens[0], out result))
                {
                    if (!lines[i].Contains("<DIR>"))
                    {
                        return tokens[tokens.Length - 1];
                    }
                }
                i++;
            }

            return "";
        }
0
ответ дан 3 December 2019 в 01:00
поделиться

Если вы управляете каталогом (то есть, если ваши программы отвечают за создание и обслуживание всех файлов в этом каталоге), вам следует рассмотреть возможность отслеживания метаданных о каждом файле отдельно; возможно в базе данных.

Фактически, в этом может помочь тип столбца FileStream в SQL Server 2008. Вы можете создать таблицу, содержащую столбцы для имени файла, даты создания, даты изменения и столбец FileStream для содержимого. Вы можете найти такие вещи, как самый старый файл, используя индексы в столбцах метаданных. Вы можете найти контент, используя столбец FileStream.

12
ответ дан 3 December 2019 в 01:00
поделиться

Посмотрите, не было бы проще выполнить оболочку для скрытого процесса и перенаправить выходной поток на вход и использовать dir / od , который сортирует по дате / времени, использование тире меняет порядок действий ....

Edit: вот пример кода, чтобы сделать это ... быстро и грязно ...

public class TestDir
    {
        private StringBuilder sbRedirectedOutput = new StringBuilder();
        public string OutputData
        {
            get { return this.sbRedirectedOutput.ToString(); }
        }
        public void Run()
        {
            System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo();
            ps.FileName = "cmd";
            ps.ErrorDialog = false;
            ps.Arguments = string.Format("dir {0} /o-d", path_name);
            ps.CreateNoWindow = true;
            ps.UseShellExecute = false;
            ps.RedirectStandardOutput = true;
            ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;

            using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
            {
                proc.StartInfo = ps;
                proc.Exited += new EventHandler(proc_Exited);
                proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived);
                proc.Start();
                proc.WaitForExit();
                proc.BeginOutputReadLine();
                while (!proc.HasExited) ;
            }
        }

        void proc_Exited(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended");
        }

        void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (e.Data != null) this.sbRedirectedOutput.Append(e.Data + Environment.NewLine);
            //System.Diagnostics.Debug.WriteLine("proc_OutputDataReceived: Data: " + e.Data);
        }
    }

Самый первый 4 или 5 строк объекта StringBuilder sbRedirectedOutput можно вырезать, тогда после этого строка будет содержать самое старое имя файла и ее будет довольно легко разобрать ....

0
ответ дан 3 December 2019 в 01:00
поделиться

Как ни странно, это отлично сработало в моем каталоге с более чем 3000 jpg-файлов:

DirectoryInfo di = new DirectoryInfo(dpath);
FileInfo[] rgFiles = di.GetFiles("*.jpg");
FileInfo firstfile = rgFiles[0];
FileInfo lastfile = rgFiles[rgFiles.Length - 1];
DateTime oldestfiletime = firstfile.CreationTime;
DateTime newestfiletime = lastfile.CreationTime;
1
ответ дан 3 December 2019 в 01:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: