我想检测目录树中的重复文件。如果找到两个相同的文件,则只保留其中一个副本,并删除剩余的重复项以节省磁盘空间。
重复意味着文件名和路径可能不同的内容相同的文件。
我正在考虑为此目的使用哈希算法但是有可能不同的文件具有相同的哈希值,所以我需要一些额外的机制来告诉我即使哈希值是相同的,文件也不一样因为我不想删除两个不同的文件。
您会使用哪种额外快速可靠的机制?
答案 0 :(得分:15)
计算哈希将使您的程序运行缓慢。您还可以检查文件大小。所有重复文件应具有相同的文件大小。如果他们共享相同的文件大小应用哈希检查。它会让你的程序表现得很快。
可以有更多步骤。
您添加的条件越多,它的执行速度就越快,您就可以避免这种方式(哈希)。
答案 1 :(得分:2)
这取决于您要比较的文件。
A)最糟糕的情况是:
例如,如果你有:
然后你会:
但是,如果你有相同的场景,但首先导出文件的哈希,你会:
我认为最糟糕的情况是不典型。
B)典型的案例场景是:
例如,如果你有:
然后你会:
如果您期望100%倍数,则散列不会比直接二进制比较更有效。鉴于您应该期望< 100%倍数,散列效率将低于直接二进制比较。
C)重复比较
这是例外。为所有文件构建散列+长度+路径数据库将加速重复比较。但好处是微不足道的。它最初需要100%读取文件并存储哈希数据库。新文件需要100%读取然后添加到数据库中,如果匹配,仍然需要直接二进制比较作为比较的最后一步(以排除哈希冲突)。即使大多数文件的大小不同,当在目标文件夹中创建新文件时,它可能与现有文件大小匹配,并且可以快速排除在直接比较之外。
总结:
答案 2 :(得分:1)
哈希解决方案很好 - 您只需要执行一个冲突机制来处理两个散列为相同值的元素。 [chaining或open addressing]。
只是迭代地添加元素 - 如果您的实现检测到存在欺骗 - 它将不会将其添加到哈希集。 如果在尝试添加元素后未更改集的大小,您将知道元素是欺骗。
很可能已经在您的语言中实现了这种数据结构 - 例如java中的HashSet和C ++中的unordered_set。
答案 3 :(得分:1)
如果您使用SHA-1或更好的SHA-256或更高版本的哈希算法,我真的怀疑您是否会为两个不同的文件获得相同的哈希值。 SHA是一种加密哈希函数,用于Git等版本控制系统。所以你可以放心,它会为你完成这项工作。
但如果您仍需要进行额外检查,可以按照以下两个步骤进行操作
1)解析标题 - 这是一个非常难以破解的cookie,因为不同的格式可能有不同的标题长度
2)进行一些健全性检查 - 文件大小,读取随机文件位置并尝试检查它们是否相同
答案 4 :(得分:1)
这是md5sum的典型输出:
0c9990e3d02f33d1ea2d63afb3f17c71
如果你不必担心故意伪造文件,那么第二个随机文件匹配的可能性是
1/(decimal(0xfffffffffffffffffffffffffffffff)+1)
如果您将文件大小考虑为额外的测试,那么您的确定性会增加,两个文件都适合。您可能会添加越来越多的测量值,但在这样的辩论中,逐位比较将成为最后一个词。出于实际目的,md5sum应该足够了。
答案 5 :(得分:0)
/// ------------------------------------------------------------------------------------------------------------------------
/// <summary>
/// Writes duplicate files to a List<String>
/// </summary>
private void CompareDirectory(string[] files)
{
for (int i = 0; i < files.Length; i++)
{
FileInfo one = new FileInfo(files[i]); // Here's a spot for a progressbar or something
for (int i2 = 0; i2 < files.Length; i2++)
{
if (i != i2 && !duplicatePathsOne.Contains(files[i2])) // In order to prevent duplicate entries
{
FileInfo two = new FileInfo(files[i2]);
if (FilesAreEqual_OneByte(one, two))
{
duplicatePathsOne.Add(files[i]);
duplicateNamesOne.Add(Path.GetFileName(files[i]));
duplicatePathsTwo.Add(files[i2]);
duplicateNamesTwo.Add(Path.GetFileName(files[i2]));
}
}
}
}
}
/// ------------------------------------------------------------------------------------------------------------------------
/// <summary>
/// Compares files by binary
/// </summary>
private static bool FilesAreEqual_OneByte(FileInfo first, FileInfo second)
{
if (first.Length != second.Length)
return false;
using (FileStream fs1 = first.OpenRead())
using (FileStream fs2 = second.OpenRead())
{
for (int i = 0; i < first.Length; i++)
{
if (fs1.ReadByte() != fs2.ReadByte())
return false;
}
}
return true;
}
答案 6 :(得分:0)
这是一个VBS脚本,它将生成CSV文件,以根据MD5文件校验和和文件大小显示文件夹中的重复文件。
Set fso = CreateObject("Scripting.FileSystemObject")
Dim dic: Set dic = CreateObject("Scripting.Dictionary")
Dim oMD5: Set oMD5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
Dim oLog 'As Scripting.TextStream
Set oArgs = WScript.Arguments
If oArgs.Count = 1 Then
sFolderPath = GetFolderPath()
Set oLog = fso.CreateTextFile(sFolderPath & "\DublicateFiles.csv", True)
oLog.Write "sep=" & vbTab & vbCrLf
CheckFolder oArgs(I)
oLog.Close
Msgbox "Done!"
Else
Msgbox "Drop Folder"
End If
Sub CheckFolder(sFolderPath)
Dim sKey
Dim oFolder 'As Scripting.Folder
Set oFolder = fso.GetFolder(sFolderPath)
For Each oFile In oFolder.Files
'sKey = oFile.Name & " - " & oFile.Size
sKey = GetMd5(oFile.Path) & " - " & oFile.Size
If dic.Exists(sKey) = False Then
dic.Add sKey, oFile.Path
Else
oLog.Write oFile.Path & vbTab & dic(sKey) & vbCrLf
End If
Next
For Each oChildFolder In oFolder.SubFolders
CheckFolder oChildFolder.Path
Next
End Sub
Function GetFolderPath()
Dim oFile 'As Scripting.File
Set oFile = fso.GetFile(WScript.ScriptFullName)
GetFolderPath = oFile.ParentFolder
End Function
Function GetMd5(filename)
Dim oXml, oElement
oMD5.ComputeHash_2(GetBinaryFile(filename))
Set oXml = CreateObject("MSXML2.DOMDocument")
Set oElement = oXml.CreateElement("tmp")
oElement.DataType = "bin.hex"
oElement.NodeTypedValue = oMD5.Hash
GetMd5 = oElement.Text
End Function
Function GetBinaryFile(filename)
Dim oStream: Set oStream = CreateObject("ADODB.Stream")
oStream.Type = 1 'adTypeBinary
oStream.Open
oStream.LoadFromFile filename
GetBinaryFile= oStream.Read
oStream.Close
Set oStream = Nothing
End Function
这是一个VBS脚本,它将生成CSV文件,以根据文件名和大小显示文件夹中的重复文件。
Set fso = CreateObject("Scripting.FileSystemObject")
Dim dic: Set dic = CreateObject("Scripting.Dictionary")
Dim oLog 'As Scripting.TextStream
Set oArgs = WScript.Arguments
If oArgs.Count = 1 Then
sFolderPath = GetFolderPath()
Set oLog = fso.CreateTextFile(sFolderPath & "\DublicateFiles.csv", True)
oLog.Write "sep=" & vbTab & vbCrLf
CheckFolder oArgs(I)
oLog.Close
Msgbox "Done!"
Else
Msgbox "Drop Folder"
End If
Sub CheckFolder(sFolderPath)
Dim sKey
Dim oFolder 'As Scripting.Folder
Set oFolder = fso.GetFolder(sFolderPath)
For Each oFile In oFolder.Files
sKey = oFile.Name & " - " & oFile.Size
If dic.Exists(sKey) = False Then
dic.Add sKey, oFile.Path
Else
oLog.Write oFile.Path & vbTab & dic(sKey) & vbCrLf
End If
Next
For Each oChildFolder In oFolder.SubFolders
CheckFolder oChildFolder.Path
Next
End Sub
Function GetFolderPath()
Dim oFile 'As Scripting.File
Set oFile = fso.GetFile(WScript.ScriptFullName)
GetFolderPath = oFile.ParentFolder
End Function