文件枚举问题

时间:2009-09-16 16:23:07

标签: c# .net file-io visual-studio-2008

我正在使用VSTS 2008 + C#+ .Net 3.5来开发控制台应用程序。我需要枚举当前文件夹中最近的50个文件(读取文件内容,获取文件元数据,如文件名,创建时间等)。由于当前文件夹有大约5,000个文件,并且如果我使用Directory.GetFiles API,所有5,000个文件的元数据信息将被读入内存。我认为这是一种浪费,因为我只需要访问最近的50个文件。

任何解决方案只能访问当前目录中的50个最新文件?

2 个答案:

答案 0 :(得分:3)

此解决方案仍会加载有关所有文件的元数据,但我认为它对于大多数用途来说足够快。以下代码报告在Windows \ System32目录(~2500个文件)中枚举50个最近更新的文件大约需要50毫秒。除非代码运行频繁,否则我可能不会花时间优化它:

FileInfo[] files = (new DirectoryInfo(@"C:\WINDOWS\System32")).GetFiles();
Stopwatch sw = new Stopwatch();
sw.Start();
IEnumerable<FileInfo> recentFiles = files.OrderByDescending(
                                              fi => fi.LastWriteTime).Take(50);
List<FileInfo> list = recentFiles.ToList();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
list.ForEach(fi => Console.WriteLine(fi.Name));

<强>更新

基于有关在文件名中使用日期/时间的注释中的讨论:请注意Directory.GetFiles不会加载有关文件的元数据;它只返回一个带文件名的字符串数组(DirectoryInfo.GetFiles另一方面返回FileInfo个对象的数组)。因此,如果您的文件名中有日期和时间(最好采用适合排序的格式,例如yyyyMMdd-HHmmss或类似的东西),您可以使用Directory.GetFiles来获取文件名,排序下降,然后从列表中首先选择50:

string[] files = Directory.GetFiles(pathToLogFiles);
IEnumerable<string> recentFiles = files.OrderByDescending(s => s).Take(50);
List<string> recentFiles = recentFiles.ToList();

然后遍历列表并从每个文件加载所需的任何数据。

答案 1 :(得分:1)

我真的不确定它值得你这么做...考虑以下程序:

    class DateCompare : IComparer<FileInfo>
    {
        public int Compare(FileInfo a, FileInfo b)
        { 
            int result = a.LastWriteTime.CompareTo(b.LastWriteTime);
            if (result == 0)
                return StringComparer.OrdinalIgnoreCase.Compare(a.FullName, b.FullName);
            return result;
        }
    }

    public static void Main(string[] args)
    {
        DirectoryInfo root = new DirectoryInfo("c:\\Projects\\");
        DateTime start = DateTime.Now;
        long memory = GC.GetTotalMemory(false);
        FileInfo[] allfiles = root.GetFiles("*", SearchOption.AllDirectories);
        DateTime sortStart = DateTime.Now;
        List<FileInfo> files = new List<FileInfo>(20000);
        IComparer<FileInfo> cmp = new DateCompare();
        foreach (FileInfo file in allfiles)
        {
            int pos = ~files.BinarySearch(file, cmp);
            files.Insert(pos, file);
        }
        Console.WriteLine("Count = {0:#,###}, Read = {1}, Sort = {2}, Memory = {3:#,###}", files.Count, sortStart - start, DateTime.Now - sortStart, GC.GetTotalMemory(false) - memory);
    }

这是上述程序的输出:

Count = 16,357, Read = 00:00:03.5793579, Sort = 00:00:06.7776777, Memory = 5,758,976
Count = 16,357, Read = 00:00:03.2173217, Sort = 00:00:06.1616161, Memory = 7,339,920
Count = 16,357, Read = 00:00:03.5083508, Sort = 00:00:06.7556755, Memory = 10,346,504

在3秒内运行,分配5~10mb,同时抓取6,931个目录并返回16k文件名。这是你谈论的音量的三倍,我打赌大部分时间都在抓取目录树(我没有一个包含5k文件的目录)。如果你可以通过匹配我建议的文件名来丢弃文件,那么最糟糕的费用总是那样。