【问题标题】:Compare adjacent list items比较相邻的列表项
【发布时间】:2015-09-09 17:38:39
【问题描述】:

我正在编写一个重复文件检测器。为了确定两个文件是否重复,我计算了一个 CRC32 校验和。由于这可能是一项昂贵的操作,我只想计算具有匹配大小的另一个文件的文件的校验和。我已经按大小对文件列表进行了排序,并且正在循环将每个元素与其上方和下方的元素进行比较。不幸的是,开头和结尾都存在问题,因为分别没有上一个或下一个文件。我可以使用 if 语句来解决这个问题,但感觉很笨拙。这是我的代码:

    public void GetCRCs(List<DupInfo> dupInfos)
    {
        var crc = new Crc32();
        for (int i = 0; i < dupInfos.Count(); i++)
        {
            if (dupInfos[i].Size == dupInfos[i - 1].Size || dupInfos[i].Size == dupInfos[i + 1].Size)
            {
                dupInfos[i].CheckSum = crc.ComputeChecksum(File.ReadAllBytes(dupInfos[i].FullName));
            }
        }
    }

我的问题是:

  1. 如何在不出现越界错误的情况下将每个条目与其相邻条目进行比较?

  2. 我应该为此使用循环,还是有更好的 LINQ 或其他功能?

注意:我没有包含我的其余代码以避免混乱。如果你想看,我可以附上。

【问题讨论】:

  • 而不是从 0 开始从 1 开始并在 dupInfos.Count() -1 结束
  • 另一个您可能没有考虑过的问题... 如果有 4 个文件大小相同,并且第一个和第 4 个文件相同,该怎么办。您的代码会错过这些,因为它们之间还有其他大小相同的不同文件。
  • 如果您不能确定是否找到所有匹配项,则需要对每个文件大小组中的每一对进行比较,而不仅仅是上下一个。跨度>
  • 3dd - 这是我目前的策略,但感觉不优雅。我只是想知道是否有更好的方法。 DiscipleMichael - 我的计划是按大小和 CRC 排序。然后我只能再次检查相邻的文件,而不是遍历大小组中的每个文件。
  • 您不必检查整个集合中的每个可能对...只需检查一组相同文件大小中的每个可能对

标签: c# linq list loops


【解决方案1】:

你能在你的两个列表之间做一个联合吗?如果您有一个文件名列表并进行联合,则它应该只产生一个重叠文件的列表。如果你愿意,我可以写一个例子,但这个链接应该给你一个大致的想法。

https://stackoverflow.com/a/13505715/1856992

编辑:抱歉,出于某种原因,我以为您是在比较文件名而不是大小。

所以这里给你一个实际的答案。

using System;
using System.Collections.Generic;
using System.Linq;


public class ObjectWithSize
{
    public int Size {get; set;}
    public ObjectWithSize(int size)
    {
        Size = size;
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("start");
        var list = new List<ObjectWithSize>();
        list.Add(new ObjectWithSize(12));
        list.Add(new ObjectWithSize(13));
        list.Add(new ObjectWithSize(14));
        list.Add(new ObjectWithSize(14));
        list.Add(new ObjectWithSize(18));
        list.Add(new ObjectWithSize(15));
        list.Add(new ObjectWithSize(15));
        var duplicates = list.GroupBy(x=>x.Size)
              .Where(g=>g.Count()>1);
        foreach (var dup in duplicates)
            foreach (var objWithSize in dup)
                Console.WriteLine(objWithSize.Size);
    }
}

这将打印出来

14
14
15
15

这是一个 netFiddle。 https://dotnetfiddle.net/0ub6Bs

最后一点。我实际上认为您的答案看起来更好,并且会运行得更快。这只是 Linq 中的一个实现。

【讨论】:

  • 感谢 thinklarge,我只有一个文件列表,需要比较它们是否具有相同大小。我不认为工会是解决这个问题的正确工具。我已经考虑将我的列表加入到自己的列表中,其中的条件是索引为 +/- 1,但我不确定这是否是最佳解决方案。
  • 谢谢 Kalev,对不起,我没有仔细阅读您的描述。我对其进行了重新设计以使其适用,这是一个基于 linq 的解决方案,但它会产生一些与组和位置相关的开销。
  • 谢谢。我喜欢使用 groupby 的想法。如果一个简单的循环工作得更快,我不妨使用它。当我在 excel 中做类似的事情时,我只是在开头和结尾添加了一条记录,并且循环运行良好。不过,在 c# 中,我似乎需要一种不同的方法。
【解决方案2】:

先计算 Crcs:

// It is assumed that DupInfo.CheckSum is nullable
public void GetCRCs(List<DupInfo> dupInfos)
{
  dupInfos[0].CheckSum = null ;        
  for (int i = 1; i < dupInfos.Count(); i++)
    {
       dupInfos[i].CheckSum = null ;
       if (dupInfos[i].Size == dupInfos[i - 1].Size)
       {
         if (dupInfos[i-1].Checksum==null) dupInfos[i-1].CheckSum = crc.ComputeChecksum(File.ReadAllBytes(dupInfos[i-1].FullName));
         dupInfos[i].CheckSum = crc.ComputeChecksum(File.ReadAllBytes(dupInfos[i].FullName));
       }
    }
}

在按大小和 crc 对文件进行排序后,识别重复项:

public void GetDuplicates(List<DupInfo> dupInfos) 
{
  for (int i = dupInfos.Count();i>0 i++)
  { // loop is inverted to allow list items deletion
    if (dupInfos[i].Size     == dupInfos[i - 1].Size &&
        dupInfos[i].CheckSum != null &&
        dupInfos[i].CheckSum == dupInfos[i - 1].Checksum)
     { // i is duplicated with i-1
       ... // your code here
       ... // eventually, dupInfos.RemoveAt(i) ; 
     }
   }
}

【讨论】:

  • 是的...此外,您可以跳过校验和的唯一方法是,如果它是列表中唯一大小相同的文件。如果你真的想要,你可以在 if 块中排除这些文件。
  • 在 GetCrcs() 过程中,校验和计算仅在 2 个或更多文件具有相同大小时进行。当只有一个特定大小的文件时,其Checksum为空,在GetDuplicates()循环中测试。
  • 你的意思是最后一行说 dupInfos[i]...?关于校验和可以为空的好点。我已将其从 uint 更改为 uint?。是否有必要将其设置为 null 还是默认为 null ?对于第二部分,你的意思是我——?我喜欢反转它以允许移除的想法。在这种情况下,每个 DupInfo 只保存有关磁盘上文件的信息,因此我需要实际对其执行删除,而不是仅将其从列表中删除。不过,我也可以将其从列表中删除。
  • 是的,它的 dupInfos[i] 在两边。我刚刚编辑了建议的代码 - 抱歉,剪切和粘贴错误! - 校验和在第一部分循环的第一条指令初始化为空。
【解决方案3】:

我认为for循环应该是:for (int i = 1; i

var grps= dupInfos.GroupBy(d=>d.Size);
grps.Where(g=>g.Count>1).ToList().ForEach(g=>
{
    ...
});

【讨论】:

  • 我喜欢这个主意。我无法填写 ForEach 部分。我想为大于一的大小组中的每个 DupInfo 将 CRC 属性设置为文件的 ComputeCheckSum。你知道我会怎么做吗?
【解决方案4】:

我已经按大小对文件列表进行了排序,并且正在循环访问 将每个元素与其上方和下方的元素进行比较。

下一个合乎逻辑的步骤是按大小实际对文件进行分组。如果您有两个以上相同大小的文件,则比较连续文件并不总是足够的。相反,您需要将每个文件与每个其他相同大小的文件进行比较。

我建议采用这种方法

  1. 使用 LINQ 的 .GroupBy 创建文件大小的集合。然后.Where 只保留具有多个文件的组。

  2. 在这些组中,计算 CRC32 校验和并将其添加到已知校验和的集合中。与之前计算的校验和进行比较。如果您需要知道哪些文件具体是重复的,您可以使用由该校验和作为键的字典(您可以使用另一个 GroupBy 来实现这一点。否则一个简单的列表就足以检测到任何重复。

代码可能如下所示:

var filesSetsWithPossibleDupes = files.GroupBy(f => f.Length)
                                      .Where(group => group.Count() > 1);

foreach (var grp in filesSetsWithPossibleDupes)
{
    var checksums = new List<CRC32CheckSum>(); //or whatever type
    foreach (var file in grp)
    {
        var currentCheckSum = crc.ComputeChecksum(file);
        if (checksums.Contains(currentCheckSum))
        {
            //Found a duplicate
        }
        else
        {
            checksums.Add(currentCheckSum);
        }
    }
}

或者,如果您需要可能重复的特定对象,内部 foreach 循环可能看起来像

var filesSetsWithPossibleDupes = files.GroupBy(f => f.FileSize)
                                      .Where(grp => grp.Count() > 1);

var masterDuplicateDict = new Dictionary<DupStats, IEnumerable<DupInfo>>();
//A dictionary keyed by the basic duplicate stats
//, and whose value is a collection of the possible duplicates

foreach (var grp in filesSetsWithPossibleDupes)
{
    var likelyDuplicates = grp.GroupBy(dup => dup.Checksum)
                              .Where(g => g.Count() > 1);
    //Same GroupBy logic, but applied to the checksum (instead of file size)

    foreach(var dupGrp in likelyDuplicates)
    {
        //Create the key for the dictionary (your code is likely different)
        var sample = dupGrp.First();
        var key = new DupStats() {FileSize = sample.FileSize, Checksum = sample.Checksum};
        masterDuplicateDict.Add(key, dupGrp);
    }
}

A demo这个想法。

【讨论】:

  • 感谢 ryanyuyu,我喜欢使用 groupby 的想法。我试图弄清楚如何应用第 2 步。每个 DupInfo 都包含一个文件路径、大小、校验和字段和一个比较文件夹字段(以防用户只想比较由 n 级分隔的文件)。我只想向用户显示可能重复的文件(相同的大小、校验和和比较文件夹),分组为集合,并让他们选择要删除的文件。我喜欢为以前的校验和使用字典的想法,并试图弄清楚如何从中获取到我显示的重复组列表。
  • 谢谢你的想法。我知道这是一个单独的问题,所以如果我需要我会发布它,但我目前正在使用 ListView (WPF) 进行显示。我不确定如何将字典/数据加载到列表视图中进行显示,或者即使列表视图是正确的工具。
  • @KalevMaricq 是的,这是一个单独的问题。如果有帮助,您始终可以包含指向此问题的链接以获取上下文。
猜你喜欢
  • 1970-01-01
  • 2023-04-08
  • 2019-05-05
  • 2020-08-25
  • 2021-04-28
  • 2020-06-17
  • 2019-10-09
  • 2012-05-04
  • 2013-10-24
相关资源
最近更新 更多