2011-06-20 49 views
3

我的C#3.0應用程序應該遍歷文件夾並在其中執行一些操作。爲了顯示有意義的進展,我需要知道文件夾總數。Quicky估計一些子文件夾

如果我使用Directory.GetDirectoriesAllDirectories選項,我的2Tb硬盤驅動器需要很長時間,大約有100K個文件夾,即使對於該操作,我也應該提供一個進度!我能做的唯一有意義的事情是使用遞歸Directory.GetDirectories並向用戶提供一些已經找到的目錄。但是,這需要比第一種方法更長的時間。

我相信,兩種方法都太慢了。有沒有辦法讓這個號碼更快?例如。從PInvoke使用某些文件表?任何其他想法?

回答

1

這類事情很難做到。如果你只是想對進度條做一個粗略的估計,你不需要太多的粒度,對吧?我建議手動遍歷目錄樹只有一層或兩層深度,以找出有多少個第一級和第二級子目錄。然後,只要你點擊其中一個子目錄,就可以更新你的進度條。這應該給你一個有意義的進度條,而不需要太多的時間來計算。

+0

謝謝 - 這正是我的想法。如果我找不到任何其他選項(以任何魔術方式從某個神奇的地方快速拉出數字),我會選擇這種方法。 – Alex

+0

亞歷克斯:文件系統不存儲你想要的信息,所以唯一的方法就是掃描。 – Gabe

2

我的建議是簡單地向用戶展示一個無限滾動的進度條,當你獲得所有的目錄時,只有當你的應用程序完成工作時向用戶顯示實際進度。

這樣用戶就會知道應用程序正在後臺工作而一切正常。

+0

感謝您的評論,賈斯汀。這是解決問題的簡單方法。但是,正如您所知,Microsoft指導方針建議避免無限進度條。瞭解背後有多少工作總是很好的。 – Alex

+0

@Alex - 當工作量已知時,您只能顯示有限的進度。當你不確定時,我寧願看到一個無限的進度條,而不是一個明顯錯誤的進度條。 –

+0

@Alex,例如,在計算刪除文件數時,Windows會顯示無限進度條。準則並不是硬性規定。我認爲在這種情況下你可以原諒下面的Windows'(Windows的?Window's?)例子。 –

0

探索FindFirstFileFindNextFile API。我認爲他們會在你的情況下更快地工作

+0

爲什麼(會更快)? –

+0

是的,但即使如此,你不知道沒有遍歷整個樹的目錄數量,這是緩慢的部分。 – Gabe

+0

好主意 - Win API有時比.NET模擬器更快。但是,我想知道這個數字是否已經存在於某些NTFS/FAT表中。 – Alex

1

如果你實現這個,你會發現你的第一次預掃描是最慢的,但它會加快下一個(完整)掃描,因爲文件夾結構正在被緩存。

它可能是一個選項,只計算前N(2..4)級別中的文件夾。這可能仍然很緩慢,但它可以預測進展。假設所有較低級別包含相同數量的文件。


第2部分,有關的P/Invoke問題

你的主要成本是在這裏是真實的低級I/O時,(任何)API的開銷可以忽略不計。

您可能會從EnumerateFiles()(Fx4)替換GetFiles()而受益。對於主循環而言,與預掃描相比更是如此。

+0

感謝您的評論 - 這與Gabe已經給予的以及我也考慮過的一樣。 – Alex

0

我寫了一個非常簡單的枚舉文件。這一進展在數學上是連續的,即不管什麼時候它都不會變成較低的值。估計是基於這樣的想法,即所有文件夾都擁有相同數量的文件和子文件夾,這顯然幾乎從未如此,但只需獲得合理的想法即可。

幾乎沒有緩存,尤其是深層結構,所以這應該幾乎和直接枚舉一樣快。

public static IEnumerable<Tuple<string, float>> EnumerateFiles (string root) 
{ 
    var files = Directory.GetFiles (root); 
    var dirs = Directory.GetDirectories (root); 
    var fact = 1f/(float) (dirs.Length + 1); // this makes for a rough estimate 

    for (int i = 0; i < files.Length; i++) { 
     var file = files[i]; 
     var f = (float) i/(float) files.Length; 
     f *= fact; 
     yield return new Tuple<string, float> (file, f); 
    } 

    for (int i = 0; i < dirs.Length; i++) { 
     var dir = dirs[i]; 
     foreach (var tuple in EnumerateFiles (dir)) { 
      var f = tuple.Item2; 
      f *= fact; 
      f += (i + 1) * fact; 
      yield return new Tuple<string, float> (tuple.Item1, f); 
     } 
    } 
} 
相關問題