フォルダー内のファイルを再帰的にリストするために、次のアルゴリズムをC#コードに記述しました。
- ディレクトリとそのサブディレクトリ内のファイルのリストの反復を開始します。
- ファイル名とパスをリストに保存します。
- 現在のファイルがリスト内の他のファイルと一致する場合は、両方のファイルを重複としてマークします。
- 重複とマークされたリストからすべてのファイルをフェッチします。
- 名前と返品でグループ化します。
50,000個のファイルと12,000個のサブディレクトリを含むフォルダでは、実装が非常に遅くなります。ディスクの読み取り操作は基本的に時間のかかる作業です。LINQ.Parallel()でさえあまり役に立ちません。
実装:
class FileTuple { public string FileName { set; get; } public string ContainingFolder { set; get; } public bool HasDuplicate { set; get; } public override bool Equals(object obj) { if (this.FileName == (obj as FileTuple).FileName) return true; return false; } }
- FileTupleクラスはファイル名と含まれるディレクトリを追跡し、フラグは重複ステータスを追跡します。
- fileTuplesのコレクションで、equalsメソッドをオーバーライドして、ファイル名のみを比較しました。
次のメソッドは、重複ファイルを見つけてリストとして返します。
private List<FileTuple> FindDuplicates()
{
List<FileTuple> fileTuples = new List<FileTuple>();
//Read all files from the given path
List<string> enumeratedFiles = Directory.EnumerateFiles(txtFolderPath.Text, "*.*", SearchOption.AllDirectories).Where(str => str.Contains(".exe") || str.Contains(".zip")).AsParallel().ToList();
foreach (string filePath in enumeratedFiles)
{
var name = Path.GetFileName(filePath);
var folder = Path.GetDirectoryName(filePath);
var currentFile = new FileTuple { FileName = name, ContainingFolder = folder, HasDuplicate = false, };
int foundIndex = fileTuples.IndexOf(currentFile);
//mark both files as duplicate, if found in list
//assuming only two duplicate file
if (foundIndex != -1)
{
currentFile.HasDuplicate = true;
fileTuples[foundIndex].HasDuplicate = true;
}
//keep of track of the file navigated
fileTuples.Add(currentFile);
}
List<FileTuple> duplicateFiles = fileTuples.Where(fileTuple => fileTuple.HasDuplicate).Select(fileTuple => fileTuple).OrderBy(fileTuple => fileTuple.FileName).AsParallel().ToList();
return duplicateFiles;
}
パフォーマンスを向上させる方法を提案していただけますか。
ご協力ありがとうございました。