C#读取文件夹和文件列表:全面指南

发布于:2025-07-08 ⋅ 阅读:(14) ⋅ 点赞:(0)

C#读取文件夹和文件列表:全面指南

在 C# 开发中,经常需要获取文件夹中的文件列表或子文件夹结构,例如文件管理器、批量处理工具、备份程序等场景。本文将详细介绍 C# 中读取文件夹和文件列表的各种方法,包括基础操作、递归遍历、过滤搜索、高级属性获取等,帮助开发者根据实际需求选择最合适的实现方式。

一、基础方法:使用 Directory 类的静态方法

System.IO.Directory类提供了一系列静态方法,可快速获取文件夹和文件列表,适合简单场景使用。

1. 获取指定目录下的所有文件

using System;
using System.IO;

class Program
{
   static void Main()
   {
       string path = @"C:MyFolder";

       try
       {
           // 获取指定目录下的所有文件(仅当前目录)
           string[] files = Directory.GetFiles(path);

           Console.WriteLine($"目录 {path} 中的文件:");
           foreach (string file in files)
           {
               Console.WriteLine(file);
           }


           // 获取指定目录下的特定类型文件
           string[] txtFiles = Directory.GetFiles(path, "*.txt");

           Console.WriteLine($"n目录 {path} 中的txt文件:");
           foreach (string file in txtFiles)
           {
               Console.WriteLine(file);
           }


           // 获取指定目录及子目录中的所有文件
           string[] allFiles = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);

           Console.WriteLine($"n目录 {path} 及其子目录中的所有文件:");
           foreach (string file in allFiles)
           {
               Console.WriteLine(file);
           }
       }
       catch (DirectoryNotFoundException)
       {
           Console.WriteLine("指定的目录不存在");
       }
       catch (UnauthorizedAccessException)
       {
           Console.WriteLine("没有访问该目录的权限");
       }
       catch (PathTooLongException)
       {
           Console.WriteLine("路径过长");
       }
       catch (Exception ex)
       {
           Console.WriteLine($"发生错误: {ex.Message}");
       }
   }
}

2. 获取指定目录下的所有子文件夹

// 获取指定目录下的所有子文件夹(仅当前目录)
string[] directories = Directory.GetDirectories(path);

Console.WriteLine($"目录 {path} 中的子文件夹:");
foreach (string dir in directories)
{
   Console.WriteLine(dir);
}


// 获取指定目录及所有子目录中的子文件夹
string[] allDirectories = Directory.GetDirectories(path, "*", SearchOption.AllDirectories);
Console.WriteLine($"n目录 {path} 及其子目录中的所有文件夹:");

foreach (string dir in allDirectories)
{
   Console.WriteLine(dir);
}

Directory 类方法特点

  • 提供静态方法,无需创建实例,调用简单
  • GetFiles()GetDirectories()方法一次性返回所有结果
  • 第三个参数SearchOption指定搜索范围:
    • SearchOption.TopDirectoryOnly:仅当前目录(默认)
    • SearchOption.AllDirectories:当前目录及所有子目录

二、面向对象方法:使用 DirectoryInfo 类

DirectoryInfo类提供了面向对象的方式来操作目录,适合需要多次使用目录信息的场景,避免重复解析路径。

1. 基本使用方法

using System;
using System.IO;

class Program
{
   static void Main()
   {
       string path = @"C:MyFolder";

       DirectoryInfo dirInfo = new DirectoryInfo(path);

       try
       {
           // 检查目录是否存在
           if (!dirInfo.Exists)
           {
               Console.WriteLine("目录不存在");
               return;
           }


           // 获取当前目录下的所有文件
           FileInfo[] files = dirInfo.GetFiles();
           Console.WriteLine($"目录 {path} 中的文件:");
           foreach (FileInfo file in files)
           {
               Console.WriteLine($"{file.Name} (大小: {file.Length} 字节)");
           }

           // 获取当前目录下的所有子文件夹
           DirectoryInfo[] subDirs = dirInfo.GetDirectories();
           Console.WriteLine($"n目录 {path} 中的子文件夹:");
           foreach (DirectoryInfo subDir in subDirs)
           {
               Console.WriteLine($"{subDir.Name} (创建时间: {subDir.CreationTime})");
           }


           // 获取特定类型文件(包括子目录)
           FileInfo[] txtFiles = dirInfo.GetFiles("*.txt", SearchOption.AllDirectories);
           Console.WriteLine($"n目录 {path} 及其子目录中的txt文件:");
           foreach (FileInfo file in txtFiles)
           {
               Console.WriteLine($"{file.FullName} (修改时间: {file.LastWriteTime})");
           }
       }
       catch (UnauthorizedAccessException)
       {
           Console.WriteLine("没有访问该目录的权限");
       }
       catch (Exception ex)
       {
           Console.WriteLine($"发生错误: {ex.Message}");
       }
   }
}

DirectoryInfo 与 Directory 的区别

  • Directory是静态类,DirectoryInfo是实例类
  • DirectoryInfo的方法返回FileInfoDirectoryInfo对象,包含更多文件 / 目录属性
  • 对于多次操作同一目录,DirectoryInfo性能更好(只需解析一次路径)
  • DirectoryInfo提供更多实例属性:NameFullNameCreationTimeLastAccessTime

三、枚举方法:延迟加载提高性能

.NET 4.0 及以上版本提供了EnumerateFiles()EnumerateDirectories()方法,采用延迟加载(Lazy Loading)方式返回结果,适合处理包含大量文件的目录。

using System;
using System.IO;

class Program
{
   static void Main()
   {
       string path = @"C:MyFolder";

       try
       {
           // 枚举方式获取文件(延迟加载)
           Console.WriteLine($"枚举目录 {path} 中的所有文件:");

           int fileCount = 0;
           foreach (string file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
           {
               Console.WriteLine(file);
               fileCount++;

               // 演示:仅显示前10个文件
               if (fileCount >= 10)
               {
                   Console.WriteLine("... 更多文件 ...");
                   break;
               }

           }


           // 使用DirectoryInfo的枚举方法
           DirectoryInfo dirInfo = new DirectoryInfo(path);
           Console.WriteLine($"n枚举目录 {path} 中的所有子文件夹:");
           int dirCount = 0;
           foreach (DirectoryInfo dir in dirInfo.EnumerateDirectories("*", SearchOption.AllDirectories))
           {
               Console.WriteLine(dir.FullName);
               dirCount++;

               // 演示:仅显示前5个子文件夹
               if (dirCount >= 5)
               {
                   Console.WriteLine("... 更多文件夹 ...");
                   break;
               }
           }
       }
       catch (Exception ex)
       {
           Console.WriteLine($"发生错误: {ex.Message}");
       }
   }
}

枚举方法的优势

  • 无需一次性加载所有结果到内存,适合处理大量文件
  • 可以在遍历过程中提前终止(如示例中的 break)
  • 内存占用低,性能更好
  • 支持流式处理,适合大型目录

四、递归获取所有文件和文件夹

在某些场景下,需要自定义递归逻辑来获取文件和文件夹列表,特别是需要在遍历过程中添加自定义过滤条件时。

1. 递归获取所有文件

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

class FileLister
{
   // 递归获取所有文件
   static List<string> GetAllFiles(string path)
   {
       List<string> files = new List<string>();

       try
       {
           // 添加当前目录的文件
           files.AddRange(Directory.GetFiles(path));

           // 递归处理子目录
           string[] subDirs = Directory.GetDirectories(path);

           foreach (string dir in subDirs)
           {
               files.AddRange(GetAllFiles(dir));
           }
       }
       catch (UnauthorizedAccessException)
       {
           Console.WriteLine($"无法访问目录: {path},已跳过");
       }
       catch (PathTooLongException)
       {
           Console.WriteLine($"路径过长: {path},已跳过");
       }
       catch (IOException ex)
       {
           Console.WriteLine($"IO错误: {path} - {ex.Message},已跳过");
       }

       return files;
   }

   static void Main()
   {
       string rootPath = @"C:MyFolder";
       Console.WriteLine($"开始递归获取 {rootPath} 下的所有文件...");

       List<string> allFiles = GetAllFiles(rootPath);
       Console.WriteLine($"n共找到 {allFiles.Count} 个文件:");
       foreach (string file in allFiles)
       {
           Console.WriteLine(file);
       }
   }
}

2. 递归获取目录结构(树形展示)

using System;
using System.IO;

class DirectoryTree
{
   // 递归显示目录结构
   static void ShowDirectoryTree(string path, string indent = "")
   {
       try
       {
           // 获取当前目录的子目录
           string[] subDirs = Directory.GetDirectories(path);

           foreach (string dir in subDirs)
           {
               // 获取目录名称
               string dirName = Path.GetFileName(dir);

               // 显示目录
               Console.WriteLine($"{indent}├─ {dirName}");

               // 递归显示子目录,增加缩进
               ShowDirectoryTree(dir, indent + "│  ");
           }


           // 显示当前目录的文件
           string[] files = Directory.GetFiles(path);
           for (int i = 0; i < files.Length; i++)
           {
               string fileName = Path.GetFileName(files[i]);

               // 最后一个文件使用不同的连接线
               string line = (i == files.Length - 1 && subDirs.Length == 0)
                   ? $"{indent}└─ {fileName}"
                   : $"{indent}├─ {fileName}";

               Console.WriteLine(line);
           }
       }
       catch (UnauthorizedAccessException)
       {
           Console.WriteLine($"{indent}├─ 无法访问的目录(权限不足)");
       }
       catch (Exception ex)
       {
           Console.WriteLine($"{indent}├─ 错误: {ex.Message}");
       }
   }


   static void Main()
   {
       string rootPath = @"C:MyFolder";
       Console.WriteLine($"目录结构: {rootPath}");
       ShowDirectoryTree(rootPath);
   }
}

自定义递归的优势

  • 可以在递归过程中添加复杂的过滤逻辑
  • 可以实现特殊的错误处理策略(如跳过无权限的目录)
  • 可以构建自定义的数据结构保存文件信息
  • 可以实现进度报告或中断机制

五、过滤与搜索技巧

实际开发中,经常需要根据特定条件过滤文件或文件夹,以下是一些常用的过滤方法。

1. 使用搜索模式

// 搜索模式示例
string path = @"C:MyFolder";

// 所有文本文件
var txtFiles = Directory.GetFiles(path, "*.txt");

// 所有图片文件(多种扩展名)
var imageFiles1 = Directory.GetFiles(path, "*.jpg")
   .Concat(Directory.GetFiles(path, "*.jpeg"))
   .Concat(Directory.GetFiles(path, "*.png"))
   .Concat(Directory.GetFiles(path, "*.gif"));


// 名称以"report"开头的Excel文件
var reportFiles = Directory.GetFiles(path, "report*.xlsx", SearchOption.AllDirectories);

2. 使用 LINQ 过滤文件

using System;
using System.IO;
using System.Linq;


class FileFilter
{
   static void Main()
   {
       string path = @"C:MyFolder";

       // 使用LINQ过滤文件
       var largeFiles = new DirectoryInfo(path)
           .GetFiles("*.*", SearchOption.AllDirectories)
           .Where(f => f.Length > 1024 * 1024) // 大于1MB的文件
           .Where(f => f.LastWriteTime > DateTime.Now.AddDays(-30)) // 30天内修改过
           .OrderByDescending(f => f.Length) // 按大小降序排列
           .Take(10); // 取前10个

       Console.WriteLine("10个最大的30天内修改过的文件:");
       foreach (var file in largeFiles)
       {
           Console.WriteLine($"{file.FullName} - {file.Length / 1024} KB - {file.LastWriteTime}");
       }


       // 过滤特定属性的文件夹
       var recentDirs = new DirectoryInfo(path)
           .GetDirectories()
           .Where(d => d.CreationTime > DateTime.Now.AddMonths(-1)) // 1个月内创建的
           .OrderBy(d => d.Name); // 按名称排序


       Console.WriteLine("n1个月内创建的文件夹:");
       foreach (var dir in recentDirs)
       {
           Console.WriteLine($"{dir.Name} - 创建于: {dir.CreationTime}");
       }
   }
}

3. 高级过滤:自定义方法

using System;
using System.IO;
using System.Linq;


class AdvancedFilter
{
   // 自定义过滤方法:检查文件是否是C#源代码且包含特定字符串
   static bool IsCsFileWithKeyword(FileInfo file, string keyword)
   {
       if (file.Extension != ".cs")
           return false;


       try
       {
           string content = File.ReadAllText(file.FullName);
           return content.Contains(keyword);
       }
       catch
       {
           return false;
       }
   }

   static void Main()
   {
       string path = @"C:MyProject";
       string searchKeyword = "async";

       var resultFiles = new DirectoryInfo(path)
           .EnumerateFiles("*.*", SearchOption.AllDirectories)
           .Where(f => IsCsFileWithKeyword(f, searchKeyword));


       Console.WriteLine($"包含 '{searchKeyword}' 的C#文件:");
       foreach (var file in resultFiles)
       {
           Console.WriteLine(file.FullName);
       }
   }
}

六、获取文件和文件夹的详细属性

除了路径和名称外,有时还需要获取文件和文件夹的详细属性,如大小、创建时间、属性等。

using System;
using System.IO;
using System.Linq;


class FileProperties
{
   static void Main()
   {
       string path = @"C:MyFolder";

       if (!Directory.Exists(path))
       {
           Console.WriteLine("目录不存在");
           return;
       }

       // 获取文件夹属性
       DirectoryInfo dirInfo = new DirectoryInfo(path);
       Console.WriteLine("文件夹属性:");
       Console.WriteLine($"完整路径: {dirInfo.FullName}");
       Console.WriteLine($"名称: {dirInfo.Name}");
       Console.WriteLine($"父目录: {dirInfo.Parent?.FullName}");
       Console.WriteLine($"创建时间: {dirInfo.CreationTime}");
       Console.WriteLine($"最后访问时间: {dirInfo.LastAccessTime}");
       Console.WriteLine($"属性: {dirInfo.Attributes}");

       // 获取文件详细属性
       FileInfo[] files = dirInfo.GetFiles().OrderByDescending(f => f.Length).Take(5).ToArray();
       Console.WriteLine("n前5个最大的文件属性:");
       foreach (FileInfo file in files)
       {
           Console.WriteLine($"n文件: {file.Name}");
           Console.WriteLine($"完整路径: {file.FullName}");
           Console.WriteLine($"大小: {file.Length} 字节");
           Console.WriteLine($"创建时间: {file.CreationTime}");
           Console.WriteLine($"最后修改时间: {file.LastWriteTime}");
           Console.WriteLine($"最后访问时间: {file.LastAccessTime}");
           Console.WriteLine($"属性: {file.Attributes}");
           Console.WriteLine($"扩展名: {file.Extension}");
           Console.WriteLine($"是否只读: {file.IsReadOnly}");
       }
   }
}

常用文件属性说明

  • Attributes:文件属性组合(ReadOnly、Hidden、System、Directory 等)
  • Length:文件大小(字节)
  • CreationTime:创建时间
  • LastWriteTime:最后修改时间
  • LastAccessTime:最后访问时间
  • IsReadOnly:是否只读

七、异步获取文件列表

在 UI 应用程序或需要非阻塞操作的场景中,异步获取文件列表可以避免界面卡顿。

using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;


class AsyncFileLister
{
   // 异步获取所有文件
   static async Task<List<FileInfo>> GetAllFilesAsync(string path)
   {
       List<FileInfo> files = new List<FileInfo>();
       DirectoryInfo dirInfo = new DirectoryInfo(path);

       if (!dirInfo.Exists)
           return files;


       // 异步获取当前目录文件
       FileInfo[] currentFiles = dirInfo.GetFiles();
       files.AddRange(currentFiles);


       // 获取子目录并异步处理
       DirectoryInfo[] subDirs = dirInfo.GetDirectories();
       foreach (DirectoryInfo subDir in subDirs)
       {
           // 递归调用并等待结果
           List<FileInfo> subDirFiles = await GetAllFilesAsync(subDir.FullName);
           files.AddRange(subDirFiles);
       }

       return await Task.FromResult(files);
   }

   static async Task Main()
   {
       string path = @"C:MyFolder";
       Console.WriteLine("开始异步获取文件列表...");

       // 记录开始时间
       DateTime startTime = DateTime.Now;

       // 异步获取文件列表
       List<FileInfo> allFiles = await GetAllFilesAsync(path);

       // 计算耗时
       TimeSpan elapsed = DateTime.Now - startTime;

       Console.WriteLine($"异步获取完成,耗时 {elapsed.TotalSeconds} 秒");
       Console.WriteLine($"共找到 {allFiles.Count} 个文件");

       // 显示最大的5个文件
       var largestFiles = allFiles.OrderByDescending(f => f.Length).Take(5);
       Console.WriteLine("n最大的5个文件:");
       foreach (var file in largestFiles)
       {
           Console.WriteLine($"{file.FullName} - {file.Length / 1024 / 1024} MB");
       }
   }
}

八、处理特殊目录

C# 可以访问各种特殊目录,如系统目录、用户目录、临时目录等,这些目录的获取方式略有不同。

using System;
using System.IO;
using System.Environment;


class SpecialDirectories
{
   static void Main()
   {
       // 获取系统特殊目录
       Dictionary<string, string> specialDirs = new Dictionary<string, string>
       {
           { "系统目录", Environment.SystemDirectory },
           { "临时目录", Path.GetTempPath() },
           { "用户目录", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) },
           { "文档目录", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) },
           { "桌面目录", Environment.GetFolderPath(Environment.SpecialFolder.Desktop) },
           { "程序文件", Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) },
           { "应用数据", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) }
       };


       // 显示特殊目录并列出其中的文件
       foreach (var dir in specialDirs)
       {
           Console.WriteLine($"n{dir.Key}: {dir.Value}");

           if (Directory.Exists(dir.Value))
           {
               try
               {
                   string[] files = Directory.GetFiles(dir.Value, "*.*", SearchOption.TopDirectoryOnly);
                   Console.WriteLine($"包含 {files.Length} 个文件");

                   // 显示前5个文件
                   for (int i = 0; i < Math.Min(5, files.Length); i++)
                   {
                       Console.WriteLine($"  {Path.GetFileName(files[i])}");
                   }

                   if (files.Length > 5)
                   {
                       Console.WriteLine("  ... 更多文件 ...");
                   }
               }
               catch (Exception ex)
               {
                   Console.WriteLine($"  无法访问: {ex.Message}");
               }
           }
           else
           {
               Console.WriteLine("  目录不存在");
           }
       }
   }
}

九、注意事项与最佳实践

1. 异常处理

文件系统操作可能会遇到各种异常,必须进行适当的处理:

// 完善的异常处理示例
try
{
   string[] files = Directory.GetFiles("C:/UnknownFolder");
}
catch (DirectoryNotFoundException)
{
   // 目录不存在
}
catch (UnauthorizedAccessException)
{
   // 权限不足
}
catch (PathTooLongException)
{
   // 路径过长(超过260字符)
}
catch (IOException)
{
   // IO错误(如设备未就绪)
}
catch (Security.SecurityException)
{
   // 安全权限不足
}
catch (ArgumentException)
{
   // 路径参数无效
}

2. 性能优化

  • 处理大量文件时,优先使用EnumerateFiles()而非GetFiles()
  • 避免在循环中重复创建DirectoryInfoFileInfo实例
  • 如需多次使用文件信息,使用FileInfoDirectoryInfo缓存信息
  • 递归遍历大型目录时,考虑使用并行处理(Parallel.ForEach)
// 并行处理提高性能
string rootPath = @"C:LargeFolder";
var dirInfo = new DirectoryInfo(rootPath);
Parallel.ForEach(dirInfo.EnumerateDirectories(), subDir =>
{
   try
   {
       var files = subDir.EnumerateFiles("*.log");
       Console.WriteLine($"在 {subDir.Name} 中找到 {files.Count()} 个日志文件");
   }
   catch (Exception ex)
   {
       Console.WriteLine($"处理 {subDir.Name} 时出错: {ex.Message}");
   }
});

3. 跨平台注意事项

.NET Core/.NET 5 + 的跨平台环境中,需要注意:

  • 路径分隔符:使用Path.DirectorySeparatorChar而非硬编码 ‘’ 或 ‘/’
  • 路径长度:Linux/macOS 对路径长度限制较宽松
  • 大小写敏感性:Linux/macOS 文件系统区分大小写
  • 特殊目录:不同操作系统的特殊目录位置不同
// 跨平台路径处理
string crossPlatformPath = Path.Combine("data", "logs", "app.log");
Console.WriteLine($"跨平台路径: {crossPlatformPath}");
Console.WriteLine($"目录分隔符: {Path.DirectorySeparatorChar}");

十、总结

C# 提供了多种方式来读取文件夹和文件列表,选择合适的方法取决于具体需求:

方法 特点 适用场景
Directory静态方法 简单直接,无需创建实例 简单操作,一次性使用
DirectoryInfo实例方法 面向对象,可缓存信息 多次操作同一目录,需要详细属性
枚举方法(Enumerate* 延迟加载,内存占用低 大量文件,流式处理
自定义递归 高度可控,可添加自定义逻辑 复杂过滤,特殊遍历需求
异步方法 非阻塞,不冻结 UI 桌面应用,需要响应性

无论使用哪种方法,都应注意异常处理、性能优化和跨平台兼容性。掌握这些技术可以帮助开发者高效地实现文件管理功能,处理各种复杂的文件系统场景。


网站公告

今日签到

点亮在社区的每一天
去签到