首页 > 解决方案 > 在 .NET Core 中使用 System.IO.Enumeration 自定义目录枚举

问题描述

这篇文章的作者提到有一个更新的 API 用于更快的文件和目录枚举方法。但是,尽管这篇文章似乎提到了示例,但它们并不存在,而且我在 Google 的其他地方找不到任何使用提到的新方法的示例?

有没有人有一些示例代码可以枚举给定文件夹和所有子文件夹/文件以返回诸如大小和上次访问等信息?

标签: c#vb.net.net-core

解决方案


我明白为什么您发现很难弄清楚这一点 - 周围没有太多信息,所以我最终挖掘了源代码以找出问题所在。对于以下示例,我使用此目录结构:

D:\Root\
|- Child\
|  |- Grandchild\
|  |  |- Grandchild.jpg
|  |  |- Grandchild.txt
|  |- Child.html
|  |- Child.txt
|- Root.txt

并导入以下命名空间:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Enumeration;

示例 1:递归列出所有文件和子目录

var enumeration = new FileSystemEnumerable<FileSystemInfo>(
    @"D:\Root",
    // What's returned here has to match the generic type
    // passed in when creating the FileSystemEnumerable.
    // As DirectoryInfo inherits from FileSystemInfo, we
    // can use FileSystemInfo for both directories and files.
    (ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
    // Should be self-explanatory.
    new EnumerationOptions() { RecurseSubdirectories = true })
{
    // We want to include everything: all directories and files.
    ShouldIncludePredicate = (ref FileSystemEntry entry) => true
};

作为FileSystemEnumerable<T>implements IEnumerable<T>,对于我们的示例,这是一个迭代集合的情况FileSystemInfo- 这里没有什么特别的:

foreach (var item in enumeration)
{
    Console.WriteLine($"{item.FullName} - {item.LastAccessTime}");
}

打印:

D:\Root\Child - 03/10/2020 18:34:16
D:\Root\Root.txt - 03/10/2020 17:39:54
D:\Root\Child\Child.html - 03/10/2020 18:27:20
D:\Root\Child\Child.txt - 03/10/2020 17:40:15
D:\Root\Child\Grandchild - 03/10/2020 18:44:36
D:\Root\Child\Grandchild\Grandchild.jpg - 03/10/2020 18:44:28
D:\Root\Child\Grandchild\Grandchild.txt - 03/10/2020 17:40:10

你会注意到几件事:

  1. 即使在递归时,从调用者的角度来看,它也是一个扁平结构
  2. 结果似乎首先按字母顺序按子目录排序,然后按字母顺序按文件名排序

示例 2:递归列出所有子目录

// We're returning strings this time.
var enumeration = new FileSystemEnumerable<string>(
    @"D:\Root",
    // Returns the full path to the directory.
    (ref FileSystemEntry entry) => entry.ToFullPath(),
    new EnumerationOptions() { RecurseSubdirectories = true })
{
    // Only include directories in the result set.
    ShouldIncludePredicate = (ref FileSystemEntry entry) => entry.IsDirectory
};

运行这个:

foreach (var item in enumeration)
{
    Console.WriteLine(item);
}

印刷:

D:\Root\Child
D:\Root\Child\Grandchild

示例 3:按扩展名过滤文件

var extensions = new List<string> { ".html", ".jpg" };

// Back to using FileSystemInfo.
var enumeration = new FileSystemEnumerable<FileSystemInfo>(
    @"D:\Root",
    (ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
    new EnumerationOptions() { RecurseSubdirectories = true })
{
    ShouldIncludePredicate = (ref FileSystemEntry entry) =>
    {
        // Skip directories.
        if (entry.IsDirectory) 
        {
            return false;
        }
        
        foreach (string extension in extensions)
        {
            var fileExtension = Path.GetExtension(entry.FileName);
            if (fileExtension.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
            {
                // Include the file if it matches one of our extensions.
                return true;
            }
        }

        // Doesn't match, so exclude it.
        return false;
    }
};

运行这个:

foreach (var item in enumeration)
{
    Console.WriteLine(item.FullName);
}

印刷:

D:\Root\Child\Child.html
D:\Root\Child\Grandchild\Grandchild.jpg

示例 4:从递归中排除某些目录

我刚刚找到另一个代表,它控制递归行为。假设我们修改原始目录结构以包含一个新目录,其中包含我们想要忽略的文件:

D:\Root\
|- Child\
|  |- Grandchild\
|  |  |- Grandchild.jpg
|  |  |- Grandchild.txt
|  |- GrandchildIgnore\
|  |  |- GrandchildIgnore.txt
|  |- Child.html
|  |- Child.txt
|- Root.txt

要获取所有其他文件的列表,同时忽略新目录中的文件,我们可以设置ShouldRecursePredicate

var enumeration = new FileSystemEnumerable<FileSystemInfo>(
    @"D:\Root",
    (ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
    new EnumerationOptions() { RecurseSubdirectories = true })
{
    ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory,
    // If the directory's name ends with ignore, skip it.
    ShouldRecursePredicate = (ref FileSystemEntry entry) => !entry.ToFullPath().EndsWith("Ignore", StringComparison.OrdinalIgnoreCase))
};

运行这个:

foreach (var item in enumeration)
{
    Console.WriteLine(item.FullName);
}

印刷:

D:\Root\Root.txt
D:\Root\Child\Child.html
D:\Root\Child\Child.txt
D:\Root\Child\Grandchild\Grandchild.jpg
D:\Root\Child\Grandchild\Grandchild.txt

如我们所愿,不包括目录。

示例 5:获取文件的长度

var enumeration = new FileSystemEnumerable<(long, string)>(
    @"D:\Root",
    (ref FileSystemEntry entry) => (entry.Length, entry.FileName.ToString()))
{
    ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory
};

给定 1D:\Root个 122 字节的文件,运行:

foreach (var (length, fileName) in enumeration)
{
    Console.WriteLine($"Name: {fileName} Length: {length}");
}

印刷:

Name: Root.txt Length: 122

推荐阅读