2010-09-24 44 views
0

在此先感谢您的任何帮助。我甚至不确定这是否可行,但我试图使用它们的哈希来获取重复文件列表,以确定与哈希关联的文件列表。从Linq查询返回字典<FileHash,string []>

我有下面这样:

Dictionary<FileHash, string[]> FindDuplicateFiles(string searchFolder) 
{ 
    Directory.GetFiles(searchFolder, "*.*") 
     .Select(
      f => new 
        { 
         FileName = f, 
         FileHash = Encoding.UTF8.GetString(new SHA1Managed() 
                   .ComputeHash(new FileStream(f, 
                          FileMode. 
                           OpenOrCreate, 
                          FileAccess.Read))) 
        }) 
     .GroupBy(f => f.FileHash) 
     .Select(g => new 
         { 
          FileHash = g.Key, 
          Files = g.Select(z => z.FileName).ToList() 
         }) 
     .GroupBy(f => f.FileHash) 
     .Select(g => new {FileHash = g.Key, Files = g.Select(z => z.Files).ToArray()}); 

它编译罚款,但我只是好奇,是否有甚至操纵的结果返回一个字典的方式。

任何建议,替代品,批评将不胜感激。

+0

如果你切换到C#您应该使用EnumerateFiles代替的GetFiles 4已经。 – 2010-09-24 22:26:47

+1

那些最后一行是重复的,是故意的?更好的编辑。 – 2010-09-24 22:28:03

+1

请注意,您无法将任何随机字节序列(例如SHA-1哈希)解码为字符串。 **不是每个字节序列都是有效的UTF-8!** – dtb 2010-09-24 22:30:41

回答

0

已经有一个扩展方法可以做到这一点。只需在现有查询的坚持到底这样的:

.ToDictionary(x => x.FileHash, x => x.Files); 

然而:使用Encoding.UTF8.GetString任意的二进制数据转换成字符串是一个非常糟糕的主意。改为使用Convert.ToBase64String。哈希是而不是 UTF-8编码的字符串,所以不要把它当作一个。

你也通过哈希分组两次,我怀疑是不是真的想要做什么。

或者,删除以前GroupBy调用和使用Lookup来代替:

var query = Directory.GetFiles(searchFolder, "*.*") 
        .Select(f => new { 
         FileName = f, 
         FileHash = Convert.ToBase64String(
          new SHA1Managed().ComputeHash(...)) 
         }) 
        .ToLookup(x => x.FileHash, x => x.FileName); 

这会给你一个Lookup<string, string>,这基本上是通过哈希分组的文件。

还有一点需要注意:我怀疑你会用这种方法打开文件流。我建议你编写一个小的单独的方法来根据文件名来计算文件的散列值,但要确保关闭流(以正常方式使用using声明)。这也将最终使你的查询更加简单 - 沿着线的东西:

var query = Directory.GetFiles(searchFolder) 
        .ToLookup(x => ComputeHash(x)); 

很难进一步大幅简化它比:)

+0

是的,这似乎是一个更好的方法。对于阅读它的人来说,更清洁和更容易找出我想要做的事情。我想我也需要阅读一下哈希算法做和不做。再次感谢您的帮助。 – 2010-09-25 00:53:11

0

创建一个扩展方法给IEnumerable < _>调用toDictionary,它将键值对的序列转换为字典。可能会在重复键上引发异常。

为什么你需要第二个GroupBy?

0

您可以使用Enumerable.ToDictionary收集LINQ查询到的字典:

var sha1 = new SHA1Managed(); 

Dictionary<string, string[]> result = 
    Directory 
     .EnumerateFiles(searchFolder) 
     .GroupBy(file => Convert.ToBase64String(sha1.ComputeHash(...))) 
     .ToDictionary(g => g.Key, g => g.ToArray()); 

一些言论:

  • 不要以为随机字节序列(如SHA-1散列)是一个有效的UTF-8字符串。
  • 考虑使用Directory.EnumerateFiles而不是Directory.GetFiles。
  • 不要忘记在计算SHA-1散列值后关闭FileStream。
  • Afaik可以重用SHA1Managed,因此您不需要为每个文件创建一个新的。
相关问题