比较相邻的列表项

时间:2015-06-23 18:44:15

标签: c# linq list loops

我正在写一个重复的文件检测器。为了确定两个文件是否重复,我计算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或其他功能?

  3. 注意:我没有包含其余的代码以避免混乱。如果你想看到它,我可以包括它。

4 个答案:

答案 0 :(得分: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) ; 
     }
   }
}

答案 1 :(得分:1)

我认为for循环应该是:for(int i = 1; i&lt; dupInfos.Count() - 1; i ++)

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

答案 2 :(得分:1)

  

我按大小对文件列表进行了排序,并循环到   比较每个元素与它上面和下面的元素。

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

我建议采用这种方法

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

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

  3. 代码可能如下所示:

    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这个想法。

答案 3 :(得分:0)

你能在两个名单之间建立联盟吗?如果您有一个文件名列表并执行联合,则应该只生成重叠文件的列表。如果你愿意,我可以写出一个例子,但这个链接应该给你一般的想法。

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的一个实现。