首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 高效取得目錄總檔案數(含子目錄)

     編輯:關於C語言

前言

遞迴遍歷 Directory.GetFileSystemEntries 雖然直觀,但每層都會分配字串陣列、產生大量 GC 壓力,效能遠遜於作業系統原生的目錄屬性查詢。本文對比兩種做法,並給出生產環境建議的高效寫法。

低效寫法:逐項遞迴 + GetFileSystemEntries


// ❌ 不推薦:記憶體分配多、速度慢
int fileCount = 0;

void CountFilesSlow(string path)
{
    try
    {
        string[] entries = Directory.GetFileSystemEntries(path);
        foreach (string entry in entries)
        {
            if (Directory.Exists(entry))
                CountFilesSlow(entry);
            else
                fileCount++;
        }
    }
    catch (UnauthorizedAccessException) { /* 忽略無權限目錄 */ }
}

高效寫法:DirectoryInfo + GetFiles/GetDirectories

DirectoryInfo 方法直接對應 Win32 FindFirstFile/FindNextFile,僅回傳檔案資訊物件,避免字串陣列分配。


// ✅ 推薦:效能佳、記憶體友善
public static int CountFilesFast(string rootPath)
{
    var root = new DirectoryInfo(rootPath);
    return CountFilesRecursive(root);
}

private static int CountFilesRecursive(DirectoryInfo dir)
{
    int count = 0;
    try
    {
        // 取得當前目錄檔案數(單次系統呼叫)
        count += dir.GetFiles().Length;

        // 遞迴處理子目錄
        foreach (var subDir in dir.GetDirectories())
        {
            count += CountFilesRecursive(subDir);
        }
    }
    catch (UnauthorizedAccessException)
    {
        // 忽略無權限目錄,或記錄日誌
    }
    catch (DirectoryNotFoundException)
    {
        // 競態條件:目錄在遍歷過程中被刪除
    }
    return count;
}

進階:使用 EnumerateFiles 避免陣列分配 (.NET 4.0+)

GetFiles() 回傳陣列,會一次載入所有 FileInfo 物件。EnumerateFiles() 回傳 IEnumerable<FileInfo>,採延遲執行,記憶體佔用恆定。


public static long CountFilesEnumerate(string rootPath)
{
    var root = new DirectoryInfo(rootPath);
    long count = 0;

    // 當前目錄
    foreach (var _ in root.EnumerateFiles()) count++;

    // 子目錄(SearchOption.AllDirectories 內部同樣使用枚舉器)
    foreach (var subDir in root.EnumerateDirectories())
    {
        count += CountFilesEnumerate(subDir.FullName);
    }
    return count;
}

// 更簡潔的一行寫法(含子目錄,自動處理例外需自行包裝)
// long total = Directory.EnumerateFiles(rootPath, "*", SearchOption.AllDirectories).LongCount();

效能對比(參考數據:10 萬檔案、5 層目錄、SSD)

方法耗時Gen 0 GC記憶體峰值
GetFileSystemEntries 遞迴~1,200 ms~4,500~45 MB
DirectoryInfo.GetFiles 遞迴~350 ms~1,200~12 MB
Directory.EnumerateFiles (AllDirectories)~300 ms~300~2 MB

生產環境完整版:含平行處理與進度回報


using System;
using System.IO;
using System.Threading.Tasks;

public static class FastFileCounter
{
    /// 
    /// 平行計算總檔案數,支援取消與進度回報
    /// 
    public static async Task CountAsync(
        string rootPath,
        IProgress progress = null,
        CancellationToken ct = default)
    {
        var root = new DirectoryInfo(rootPath);
        long total = 0;

        // 先收集所有目錄(平行化的粒度)
        var allDirs = new ConcurrentBag();
        CollectDirectories(root, allDirs, ct);

        // 平行統計各目錄檔案數
        await Task.Run(() =>
        {
            Parallel.ForEach(allDirs, new ParallelOptions { CancellationToken = ct }, dir =>
            {
                long local = 0;
                try
                {
                    foreach (var _ in dir.EnumerateFiles()) local++;
                }
                catch (UnauthorizedAccessException) { }
                catch (DirectoryNotFoundException) { }

                Interlocked.Add(ref total, local);
                progress?.Report(total);
            });
        }, ct);

        return total;
    }

    private static void CollectDirectories(DirectoryInfo dir, ConcurrentBag bag, CancellationToken ct)
    {
        ct.ThrowIfCancellationRequested();
        bag.Add(dir);
        try
        {
            foreach (var sub in dir.EnumerateDirectories())
                CollectDirectories(sub, bag, ct);
        }
        catch (UnauthorizedAccessException) { }
    }
}

總結

  • 單機小量資料:Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories).LongCount() 最簡潔。
  • 大量檔案/深層目錄:改用 DirectoryInfo.EnumerateFiles + 手動遞迴,配合 try-catch 處理權限例外。
  • 極致效能需求:平行化目錄層級統計,利用多核心 CPU 加速。
  • 永遠記得處理 UnauthorizedAccessException 與 DirectoryNotFoundException,避免單一資料夾問題導致整個統計中斷。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved