3

フォルダー内のファイルを再帰的にリストするために、次のアルゴリズムをC#コードに記述しました。

  1. ディレクトリとそのサブディレクトリ内のファイルのリストの反復を開始します。
  2. ファイル名とパスをリストに保存します。
  3. 現在のファイルがリスト内の他のファイルと一致する場合は、両方のファイルを重複としてマークします。
  4. 重複とマークされたリストからすべてのファイルをフェッチします。
  5. 名前と返品でグループ化します。

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;
    }
}
  1. FileTupleクラスはファイル名と含まれるディレクトリを追跡し、フラグは重複ステータスを追跡します。
  2. 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;
    }

パフォーマンスを向上させる方法を提案していただけますか。

ご協力ありがとうございました。

4

2 に答える 2

3

パフォーマンスを向上させる方法を提案していただけますか。

明らかな改善の1つは、とを使用することDictionary<FileTuple, FileTuple>ですList<FileTuple>。そうすればIndexOf、各チェックでO(N)操作を行う必要がなくなります。また、オーバーライドする必要があることに注意してくださいGetHashCode()。これについては、すでに警告が表示されているはずです。

しかし、それが大きな違いを生むとは思えません。これはほとんどIOバウンドであると思います。

さらに、最後のフィルタリングと順序付けが重大なボトルネックになるとは思えないためAsParallel、最後のステップでを使用してもあまり効果はありません。もちろん、これらすべてを測定する必要があります。

最後に、フラグや/ :HasDuplicateのオーバーライドを必要とせずに、メソッド全体をかなり単純にすることができます。EqualsGetHashCode

private List<FileTuple> FindDuplicates()
{
    return Directory.EnumerateFiles(txtFolderPath.Text, "*.*", 
                                    SearchOption.AllDirectories)
                    .Where(str => str.Contains(".exe") || 
                           str.Contains(".zip")
                    .Select(str => new FileTuple { 
                               FileName = Path.GetFileName(str),
                               ContainingFolder = Path.GetDirectoryName(str))
                            })
                    .GroupBy(tuple => tuple.FileName)
                    .Where(g => g.Count() > 1) // Only keep duplicates
                    .OrderBy(g => g.Key)       // Order by filename
                    .SelectMany(g => g)        // Flatten groups
                    .ToList();                     
}
于 2012-10-06T21:03:44.020 に答える
1

If performance is critical I can suggesting using a third party library from http://www.voidtools.com/download.php , try downloading this tool and run a few queries, it will be lighting fast, it works by building an index of files and directories over entire file system on first run , the index constructed very fast in under a minute and takes some time both on memory and disk but after that queries will be blazing fast you can look in their C# example how to use it in your code.

于 2012-10-06T22:18:20.770 に答える