我正在写一个重复的文件检测器。为了确定两个文件是否重复,我计算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));
}
}
}
我的问题是:
如何在没有越界错误的情况下将每个条目与其邻居进行比较?
我应该为此使用循环,还是有更好的LINQ或其他功能?
注意:我没有包含其余的代码以避免混乱。如果你想看到它,我可以包括它。
答案 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)
我按大小对文件列表进行了排序,并循环到 比较每个元素与它上面和下面的元素。
下一个合乎逻辑的步骤是按大小实际对文件进行分组。如果您有两个以上相同大小的文件,则比较连续文件并不总是足够的。相反,您需要将每个文件与每个其他相同大小的文件进行比较。
我建议采用这种方法
使用LINQ的.GroupBy
创建文件大小的集合。然后.Where
仅保留具有多个文件的组。
在这些组中,计算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这个想法。
答案 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的一个实现。