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
的方法返回FileInfo
和DirectoryInfo
對象,包含更多文件 / 目錄屬性- 對于多次操作同一目錄,
DirectoryInfo
性能更好(只需解析一次路徑) DirectoryInfo
提供更多實例屬性:Name
、FullName
、CreationTime
、LastAccessTime
等
三、枚舉方法:延遲加載提高性能
.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()
- 避免在循環中重復創建
DirectoryInfo
或FileInfo
實例 - 如需多次使用文件信息,使用
FileInfo
和DirectoryInfo
緩存信息 - 遞歸遍歷大型目錄時,考慮使用并行處理(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 | 桌面應用,需要響應性 |
無論使用哪種方法,都應注意異常處理、性能優化和跨平臺兼容性。掌握這些技術可以幫助開發者高效地實現文件管理功能,處理各種復雜的文件系統場景。