C# 如何获取目录中存在的所有文件?

来源:站长工具作者:大海头衔:草根站长
导读:本期聚焦于大海创作的《C# 如何获取目录中存在的所有文件?》,敬请观看详情。枚举目录下的全部文件,最直接的办法是调用 System.IO 命名空间里的 Directory.GetFiles 方法。这个方法会返回一个字符串数组,包含指定路径下每个文件的完整路径。如果目录层级较深,还可以配合 SearchOption.AllDirectories 选项递归获取所有子目录中的文件。不过实际使用中,处理大目录或需要逐条流式读取文件信息时,Directory.EnumerateFiles 比 GetFiles 更节省内存,因为前者是延迟执行的。此外,别忘了处理 UnauthorizedAccessException 和 PathTooLongException 等常见异常,以及利用通配符过滤特定扩展名。本篇文章会从基础调用讲起,逐步覆盖递归枚举、通配符匹配、异常处理、性能对比和替代方案,适合刚接触 C# 文件操作或者想理清不同 API 差异的开发者参考。

获取目录中存在的所有文件,是文件管理、批量处理、备份工具等场景里非常基础的需求。C# 的 System.IO 命名空间提供了几种不同的方法,最常用的就是 Directory.GetFiles。它接受一个目录路径作为参数,返回该目录下所有文件的完整路径字符串数组。下面先看一段最简单的调用代码:

C# 如何获取目录中存在的所有文件?

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\Users\Public\Documents";
        string[] files = Directory.GetFiles(path);

        Console.WriteLine($"共找到 {files.Length} 个文件:");
        foreach (string file in files)
        {
            Console.WriteLine(file);
        }
    }
}

这段代码假设目录存在且程序有访问权限,否则会抛出 DirectoryNotFoundException 或 UnauthorizedAccessException。GetFiles 默认只返回当前目录下的文件,不会进入子目录。如果需要递归包含所有子目录中的文件,可以传入第二个参数 SearchOption.AllDirectories,比如 Directory.GetFiles(path, "*", SearchOption.AllDirectories)。这里的星号是通配符,表示匹配所有文件名,也可以换成 *.txt、*.jpg 等具体模式。

递归枚举与通配符过滤

当目录结构比较深,一个目录里可能还有多层子目录时,就需要递归获取全部文件。Directory.GetFiles 的 SearchOption 枚举有两个值:TopDirectoryOnly 和 AllDirectories。前者是默认行为,后者会遍历所有嵌套子目录。比如要统计整个项目文件夹里的所有 .cs 文件,可以这样写:

string root = @"D:\Projects\MyApp";
string[] csFiles = Directory.GetFiles(root, "*.cs", SearchOption.AllDirectories);

foreach (string file in csFiles)
{
    Console.WriteLine(file);
}

通配符匹配规则和 Windows 文件系统一致,星号代表零个或多个字符,问号代表单个字符。但要注意,通配符匹配只针对文件名部分,不包括路径。如果目录权限不足,GetFiles 在遇到第一个不可访问的子目录时就会抛出异常,整个方法调用失败,不会返回已经遍历到的结果。这在处理系统目录或者用户目录时很常见,因此后面会专门讨论异常处理和替代方案。

此外,通配符还可以组合使用,例如 *report?.xlsx 会匹配 report1.xlsx、reportA.xlsx 等,但不会匹配 report10.xlsx,因为问号只对应一个字符。如果希望忽略文件扩展名大小写,可以在比较时使用 StringComparison.OrdinalIgnoreCase,不过 GetFiles 在 Windows 上默认不区分大小写,因为 Windows 文件系统本身不区分大小写。

EnumerateFiles 的延迟加载特性

Directory.GetFiles 会一次性把整个目录的文件路径都加载到内存中,然后返回一个数组。对于包含几十万个文件的目录,这种做法可能会占用大量内存,甚至导致程序卡顿。.NET 4.0 引入了 Directory.EnumerateFiles,它返回一个 IEnumerable<string>,采用延迟执行机制,遍历一个获取一个,而不是一次性全部加载。

下面这段代码演示了 EnumerateFiles 的用法:

IEnumerable<string> files = Directory.EnumerateFiles(@"C:\LargeFolder", "*", SearchOption.AllDirectories);

long count = 0;
foreach (string file in files)
{
    // 逐个处理文件,内存占用更低
    count++;
    if (count % 10000 == 0)
    {
        Console.WriteLine($"已处理 {count} 个文件...");
    }
}
Console.WriteLine($"共处理 {count} 个文件");

EnumerateFiles 和 GetFiles 的签名几乎一样,也支持通配符和 SearchOption。区别在于 EnumerateFiles 在迭代过程中如果遇到权限不足的目录,同样会抛出异常。但是因为它推迟了获取时间,你可以在处理前先判断目录大小,或者配合 try-catch 跳过个别不可访问的目录。对于大目录遍历,通常建议优先使用 EnumerateFiles,因为它能显著降低内存峰值。

另一个差异是,EnumerateFiles 返回的枚举器底层使用 Windows API 的 FindFirstFile/FindNextFile 机制,因此在迭代过程中如果目录内容发生变化,可能会出现不一致的结果,这是文件系统遍历的固有问题。GetFiles 则先完成快照再返回数组,相对更稳定。

异常处理与安全遍历策略

遍历文件系统时,最常见的异常包括 DirectoryNotFoundException(目录不存在)、UnauthorizedAccessException(没有访问权限)、PathTooLongException(路径超过系统限制)以及 IOException(其他 I/O 错误)。下面是一个带异常处理的示例:

string path = @"C:\Windows\System32";
try
{
    string[] files = Directory.GetFiles(path, "*.dll", SearchOption.TopDirectoryOnly);
    foreach (string file in files)
    {
        Console.WriteLine(Path.GetFileName(file));
    }
}
catch (DirectoryNotFoundException)
{
    Console.WriteLine("指定的目录不存在。");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("没有权限访问该目录。");
}
catch (PathTooLongException)
{
    Console.WriteLine("路径过长,无法访问。");
}
catch (IOException ex)
{
    Console.WriteLine($"发生 I/O 错误:{ex.Message}");
}

如果只是需要跳过无法访问的目录继续遍历其他部分,可以结合 Directory.EnumerateDirectories 和递归方法自行实现,但要注意防止无限递归和重复访问。一个常见做法是使用 Stack<string> 或 Queue<string> 来手动管理待处理目录。下面的代码演示了一种安全的递归遍历方式:

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

class SafeFileWalker
{
    public static IEnumerable<string> SafeEnumerateFiles(string root, string pattern = "*")
    {
        var pending = new Stack<string>();
        pending.Push(root);

        while (pending.Count > 0)
        {
            string currentDir = pending.Pop();
            string[] files = null;
            string[] subDirs = null;

            try
            {
                files = Directory.GetFiles(currentDir, pattern);
                subDirs = Directory.GetDirectories(currentDir);
            }
            catch (UnauthorizedAccessException)
            {
                // 跳过无权限的目录
                continue;
            }
            catch (DirectoryNotFoundException)
            {
                continue;
            }

            foreach (string file in files)
            {
                yield return file;
            }

            foreach (string dir in subDirs)
            {
                pending.Push(dir);
            }
        }
    }
}

这段代码使用迭代器和栈结构手动控制遍历,遇到无权限目录直接跳过,不会中断整个枚举过程。需要注意的是 yield return 在异常处理块外部使用,这样可以避免编译错误。这种方案适合处理包含大量系统目录或受保护目录的场景。

获取文件的其他属性与替代方案

有时不仅需要文件路径,还需要文件大小、修改时间等信息。这时可以使用 DirectoryInfo 和 FileInfo 类。DirectoryInfo.GetFiles 返回 FileInfo 对象数组,每个 FileInfo 包含 Length、LastWriteTime、Extension 等属性。例如:

DirectoryInfo dirInfo = new DirectoryInfo(@"C:\Users\Public\Documents");
FileInfo[] fileInfos = dirInfo.GetFiles("*.pdf", SearchOption.TopDirectoryOnly);

foreach (FileInfo fi in fileInfos)
{
    Console.WriteLine($"{fi.Name} - {fi.Length} bytes - 修改时间:{fi.LastWriteTime:yyyy-MM-dd HH:mm:ss}");
}

DirectoryInfo.EnumerateFiles 同样提供了延迟加载的版本,返回 IEnumerable<FileInfo>。FileInfo 的 Length 属性在访问时会读取文件系统元数据,如果文件已被删除或移动,可能会抛出 FileNotFoundException。因此如果你的需求只是枚举文件名,建议使用字符串版本;如果需要详细的文件信息,再使用 FileInfo 版本。

除了 System.IO,.NET 还提供了 System.IO.Abstractions 这类第三方库来提升可测试性,或者使用 Microsoft.VisualBasic.FileIO.FileSystem 中的 GetFiles 方法,但基本思想相同。在实际项目中,如果目录很大且只需要处理一部分文件,可以结合 LINQ 的 Take 和 Skip 进行分页,不过要注意 EnumerateFiles 的延迟特性会保留文件系统句柄,直到枚举结束。

总体来看,C# 获取目录所有文件并不复杂,核心就是 Directory.GetFiles 和 Directory.EnumerateFiles 两个方法。理解它们的同步加载与延迟加载差异、通配符规则以及异常处理策略,能帮你写出更健壮的文件处理代码。

C#获取目录所有文件Directory.GetFiles修改时间:2026-10-02 07:08:44

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1002/64558.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。