如何使用.NET快速获取目录中最旧的文件?

时间:2010-03-08 00:25:50

标签: c# .net file-io

我有一个大约15-30万个文件的目录。我需要拉最老的一个。换句话说,首先创建的那个。有没有一种快速的方法来使用C#,除了将它们加载到一个集合然后排序?

9 个答案:

答案 0 :(得分:17)

您必须将FileInfo对象加载到集合&排序,但它是一个单行:

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

好的,两行,因为它是一个很长的陈述。

答案 1 :(得分:12)

如果您控制目录(即,如果您的程序负责创建和维护该目录中的所有文件),那么您应该考虑单独跟踪每个文件的元数据;也许在数据库中。

实际上,SQL Server 2008中的FileStream列类型可以帮助解决这个问题。您可以创建一个表,其中包含文件名,创建日期,修改日期和内容的FileStream列的列。您可以使用元数据列上的索引查找最旧的文件。您可以使用FileStream列找到内容。

答案 2 :(得分:7)

简短的回答是。 Windows文件系统不按日期索引文件,因此没有本地方法可以执行此操作,更不用说.net方式而不枚举所有这些。

答案 3 :(得分:4)

编辑:删除排序并使其成为一个功能。

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 :(得分:4)

如果没有排序,你就无法做到,但你可以做的就是让它快速。

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

答案 5 :(得分:2)

排序为O(n log n)。相反,你为什么不枚举目录?我不确定FindFirstFile() / FindNextFile()的C#等价物是什么,但你想做的是:

  • 将当前最低日期和文件名保存在本地变量中。

  • 枚举目录。

    • 如果给定文件上的日期小于局部变量,请将局部变量设置为新的日期和文件名。

答案 6 :(得分:1)

奇怪的是,这完全适用于我的目录中有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;

答案 7 :(得分:0)

看,是不是更容易发现隐藏进程并将输出流重定向到输入并使用按日期/时间排序的dir /o-d,使用破折号反转操作...

编辑:这是执行此操作的示例代码...快速而肮脏......

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

StringBuilder对象sbRedirectedOutput的前4行或5行可以被删除,然后该行包含最早的文件名,并且很容易解析....

答案 8 :(得分:0)

这是一个C#例程,可以通过生成cmd shell执行所需的操作,在指定的目录上执行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 "";
        }