1. 程式人生 > 其它 >C#使用普通方法快速遍歷檔案

C#使用普通方法快速遍歷檔案

C#使用普通方法快速遍歷檔案

起因是想製作一個給檔案貼標籤用來管理的系統,首先遇到的問題就是快速的獲取到所有想管理到的檔案,首先想到的就是EveryThing的實現方式,使用直接訪問USNJournal日誌,但是在網上搜到的C#實現方式一執行不是訪問被拒絕(How do we access MFT through C#),就是各種超出索引(使用MFT Scanner遍巡USN Journal)。猜測是因為時代久遠底層出現變化導致原本的方法需要進行一些改動才能使用,由於這方面知識實在不足,就放棄了這種實現方法。

基礎方法

隨後便寫了如下方法進行獲取

private List<FileInfo> GetFilesInfo(string dirPath)
{
    List<FileInfo> info = new List<FileInfo>();
    if (!Directory.Exists(dirPath))
    {
        message = dirPath + " is not a correct folder path!";
        return null;
    }
    Queue<string> dirq = new Queue<string>();
    dirq.Enqueue(dirPath);
    while (dirq.Count > 0)
    {
        string path = dirq.Dequeue();
        try
        {
            string[] filesPath = Directory.GetFiles(path);
            foreach (var item in filesPath)
            {
                FileInfo fi = new FileInfo(item);
                if ((fi.Attributes & FileAttributes.System) != FileAttributes.System)
                {
                    message = fi.FullName;
                    info.Add(fi);
                }
            }
            string[] directorysPath = Directory.GetDirectories(path);
            foreach (var item in directorysPath)
            {
                DirectoryInfo di = new DirectoryInfo(item);
                if ((di.Attributes & FileAttributes.System) != FileAttributes.System)
                {
                    dirq.Enqueue(item);
                }
            }
        }
        catch (Exception e)
        {
            message=e.Message + "\n" + e.Data;
            return null;
        }
    }
    return info;
}

其中

  • message為string型別變數,用於展示在UI介面(表示正在執行)
  • if ((di.Attributes & FileAttributes.System) != FileAttributes.System)這個判斷是用來回避系統檔案

結果
688461個檔案,用時:152706ms

優化

搜尋兩分鐘用時還是太長了,然後便在網上搜索快速查詢的方法,這是優化後的程式碼

private void GetFilesInfo(DirectoryInfo path)
{
    try
    {
        var files = path.EnumerateFiles();
        foreach (var item in files)
        {
            if ((item.Attributes & FileAttributes.System) != FileAttributes.System)
            {
                message = item.FullName;
                fis.Add(item);
            }
        }
        var dirs = path.EnumerateDirectories();

        Parallel.ForEach(dirs, dir =>
        {
            if ((dir.Attributes & FileAttributes.System) != FileAttributes.System)
            {
                Interlocked.Increment(ref count);
                GetFilesInfo(dir);
            }
        });

        Interlocked.Decrement(ref count);
    }
    catch (Exception e)
    {
        message=e.Message + "\n" + e.Data;
        error=true;
    }
}

其中

  • 使用EnumerateFiles和EnumerateDirectories來獲取檔案和資料夾,不在獲取時得到全部資訊(之前的的方法應該還有new的消耗,因為返回的是陣列)
  • 使用 Parallel.ForEach 來多執行緒使用遞迴方法
  • fis型別為 ConcurrentBag,就是一個多執行緒安全的連結串列
  • count型別為int,用於記錄還有多少資料夾沒有搜尋完成,搜尋前為1(根節點),用於判斷是否搜尋完成,Interlocked.Increment為執行緒安全的++,Interlocked.Decrement為執行緒安全的--
  • error為bool型別,用於判斷是否出現錯誤

結果
用時:34175ms,縮減了四分之三的時間

結論

考慮到Everything掃描同樣的磁碟也需要十秒左右,優化後的時間相對可以接受,能夠正常使用了

[Fastest way searching specific files]"https://codereview.stackexchange.com/questions/74156/fastest-way-searching-specific-files"