Skip to content

遍历文件

约 1392 字大约 5 分钟

NET

2024-03-16

某些应用程序必须读取现有文件夹下的文件夹结构,或者读取整个磁盘或连接设备的文件夹结构。以这种方式使用 Directory 类中的标准方法可能会导致问题。另一种方法是递归读取文件夹。

读取文件夹

在创建与文件系统一起使用的软件时,通常希望读取磁盘的整个文件夹结构。例如,您可能希望使用完整的目录列表填充 TreeView 控件,或者您可以读取文件夹以便将它们与另一个目录或备份副本进行比较。

Directory 类是 .NET Framework 中的标准类型,它包含一个名为 GetDirectories 的方法,该方法允许您读取给定路径中的文件夹,并在字符串数组中返回其名称。可以使用 Directory 和 File 类的其他方法来处理文件夹及其内容。下面显示了 GetDirectories 方法的示例。这将返回可在 C: 驱动器的根目录中找到的文件夹名称列表。

_注意:要运行此代码,您需要引用 System.IO 命名空间,因此请在代码中添加指令using System.IO

string[] folders = Directory.GetDirectories(@"c:\");

若要指示该方法以递归方式工作,读取文件夹、其子文件夹及其子文件夹和目录,直到文件夹结构用尽,可以添加 AllDirectories 搜索选项,如下所示: string[] folders = Directory.GetDirectories(@"c:\", "*", SearchOption.AllDirectories);

不幸的是,这有问题。其中的关键是,可以配置您尝试读取的某些文件夹,以便当前用户可能无法访问它们。该方法不会忽略您具有限制访问权限的文件夹,而是会引发 UnauthorizedAccessException 。但是,我们可以通过创建自己的递归文件夹搜索代码来规避这个问题。

文件夹递归

让我们从一些简单的文件夹递归代码开始,这些代码重新创建了 GetDirectories 方法的有缺陷的功能。我们将在控制台应用程序中创建它,并在检查它们时通过输出文件夹名称来显示其活动。在实际应用程序中,您可以构建并返回列表或数组,或者使用迭代器在需要时返回值,从而避免因提前读取大型文件夹结构而导致的延迟。

下面的代码显示了基本的递归方法。Main 方法调用 ShowAllFoldersUnder,并传入起始路径。在这种情况下,我们将显示从 C: 驱动器的根目录开始的所有文件夹。

ShowAllFoldersUnder 执行实际工作。调用此方法时,它使用 GetDirectories 的基本版本来获取所提供路径中目录的文件夹列表。然后,它会遍历这些文件夹,首先显示名称,然后调用自身,传递文件夹名称,以便更深入地查看。这种递归性质会导致检查所有文件夹,除非引发异常

static void Main(string[] args)
{
    Console.WriteLine(@"c:\");
    ShowAllFoldersUnder(@"c:\", 0);
}
 
private static void ShowAllFoldersUnder(string path, int indent)
{
    foreach (string folder in Directory.GetDirectories(path))
    {
        Console.WriteLine("{0}{1}", new string(' ', indent), Path.GetFileName(folder));
        ShowAllFoldersUnder(folder, indent + 2);
    }
}

为了避免 UnauthorizedAccessExceptions 停止执行,我们需要在 foreach 循环周围添加一个 try / catch 块。我们可以使用它来仅捕获该类型的异常并忽略它。这意味着不会显示用户无权访问的文件夹。更新后的 ShowAllFolders 代码如下所示。

private static void ShowAllFoldersUnder(string path, int indent)
{
    try
    {
        foreach (string folder in Directory.GetDirectories(path))
        {
            Console.WriteLine("{0}{1}", new string(' ', indent), Path.GetFileName(folder));
            ShowAllFoldersUnder(folder, indent + 2);
        }
    }
    catch (UnauthorizedAccessException) { }
}

重新分析点

我们必须处理的另一件事是_重新分析_点或_交接点_。Windows 操作系统使用的大多数磁盘都使用_新技术文件系统_ (NTFS) 进行格式化。这允许以有趣的方式挂载磁盘。除了通常的驱动器号分配(如 C:)之外,还可以将磁盘装载到文件夹中。这意味着,当您使用 Windows 资源管理器浏览到 C:\OtherDisk 文件夹时,您可能会看到另一个磁盘或分区的内容。

交接点在递归扫描文件夹时会产生问题;驱动器可以以创建无限目录结构的方式挂载。例如,包含 C: 驱动器的磁盘可以安装在名为“C:\Infinite”的文件夹中。如果你查看这个文件夹,你会发现C:根目录中的所有内容,包括“Infinite”文件夹。您可以继续向下钻取到该文件夹,每次看到相同的子文件夹和文件时。

为了避免无限循环,我们需要检查文件夹是否不是交接点。我们可以通过读取每个文件夹的属性并检查它是否是重分析点来做到这一点。为了获取属性,我们使用 File.GetAttributes 方法,该方法返回表示位字段枚举。然后,我们可以使用按位 AND 运算符来检查_是否存在 ReparsePoint_ 标志。

使用此信息,我们可以创建 ShowAllFoldersUnder 的最终版本,如下所示:

private static void ShowAllFoldersUnder(string path, int indent)
{
    try
    {
        if ((File.GetAttributes(path) & FileAttributes.ReparsePoint)
            != FileAttributes.ReparsePoint)
        {
            foreach (string folder in Directory.GetDirectories(path))
            {
                Console.WriteLine(
                    "{0}{1}", new string(' ', indent), Path.GetFileName(folder));
                ShowAllFoldersUnder(folder, indent + 2);
            }
        }
    }
    catch (UnauthorizedAccessException) { }
}