一、简述磁盘文件管理工具
该工具的主要功能是删除磁盘中的重复文件。如何判断两个文件的内容是否完全一致,我们采用计算文件指纹的方法,通过指纹验证的方法判断两个文件是否相同。所谓的文件指纹即数字签名。
二、 该工具实现所需要解决那些问题
- 如何判断两个文件的内容完全一致?
- 数字签名如何进行计算?
- 如何获取磁盘中的文件信息?
- 怎么去找到那些文件内容相同的文件?
三、 概述问题的解决方法
- 我们可以通过文件的数字签名来判断两个文件内容是否相同。
数字签名: 数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。
数字签名的特点:
①只能有信息的发送者才能产生的信息,无法被别人伪造。
②数字签名代表了文件的特征,文件如果发生改变,数字摘要的值也将发生变化。不同的文件将得到不同的数字摘要。
- 文件签名算法的选择
常用的数字签名方法主有:HASH算法 ,此算法主要包括MD(Message-Digest,信息摘要)和SHA(安全散列算法,Secure Hash Algorithm)两类 。
因为我们所需要判断的是文件内容是否相同,因此我们需要产生的数字签名应该需要有信息摘要这一特点,因此我们选择MD5算法,来计算文件的数字签名。
- 通过库函数的调用,我们将所输入的磁盘下的文件逐个获取,并保存在容器中。
- 我们可以将所有文件的MD5值进行计算,找出有重复出现的MD5值,找到那些文件所计算的MD5相同,将重复出现的文件保存在容器中,进行删除(至少保留一个文件)。
注意: 由于MD5算法过程是不可逆的,所以在保存文件信息时,我们需要保存文件的绝对路径,防止在最后删除时无法准确的查找到文件。
四、 上述问题的具体解决方案
4.1 MD5算法介绍
MD5是由Ron Rivest在1991设计的一种信息摘要(message-digest )算法,当给定任意长度的信息,MD5会产生一个固定的128位“指纹”或者叫信息摘要。从理论的角度,所有的信息产生的MD5值都不同,也无法通过给定的MD5值产生任何信息,即不可逆。
4.2 概述MD5算法
MD5的算法输入为以bit为单位的信息(1 byte = 8 * bit),经过处理,得到一个128bit的摘要信息。这128位的摘要信息在计算过程中分成4个32bit的子信息,存储在4个buffer(A,B,C,D)中,它们初始化为固定常量。MD5算法然后使用每一个512bit的数据块去改变A,B,C,D中值,所有的数据处理完之后,把最终的A,B,C,D值拼接在一起,组成128bit的输出。处理每一块数据有四个类似的过程,每一个过程由16个相似的操作流组成,操作流中包括非线性函数,相加以及循环左移。
4.3 具体分析MD5算法过程
- 添加填充位
在信息的最后一个数据块进行填充,并非所有的数据块都要进行填充。填充位的填充是必须执行的,至少也要填充一位。(但实际上我们最少需要填充8位,即一个字节,因为在计算机中的最小操作单位是1byte。)
填充规则: 第一个bit填充位填 ‘1’ ,后续bit填充位都填 ‘0’ ,最终使消息的总体长度满足上述要求。总之,至少要填充 1 bit,至多填充 512 bit。
- 添加bit长度
在进行添加位填充之后,要在最后添加64bit大小的文件原始长度(未进行填充的长度),如果文件的大小大于2^64,那么只去前64位。
在前两步完成之后,要保证文件的最终长度模512为0(即:文件填充之后的长度要为512的整数倍)。
在进行添加位填充之后,就会出现如下几种情况。注意:计算机所操作的最小单位是8bit(1byte)。
-
消息原始长度%512 >= 448
在这种情况下最后一块数据的的大小一定不小448,最多是508。
在最后一块数据长度大于448bit时,先填充8位的填充信息,剩余长度小于等于56bit,不满足要求的64bit。因此需要开辟新的空间,所以我们需要先将剩余的bit位上的数据全部写0。再开辟新的数据块,因为我们只使用最后的64进行文件原始数据长度的填充,因此在新数据块中前448位全部填0,最后64位我们进行填充文件长度。 -
消息原始长度%448 < 512
在这种情况下最后一块数据的大小一定小于等于440
在最后一块数据长度小于448时,我们需要先填充8位的填充信息,然后计算最后空闲的数据大小,预留64位填充文件原始长度,其余的bit位填充0。
- 初始化 MD buffer
用4个unsigned int的buffer (A, B, C ,D) 来计算摘要信息。,这些变量初始化为下面的十六进制值,低字节在前:
/*
word A: 01 23 45 67
word B: 89 ab cd ef
word C: fe dc ba 98
word D: 76 54 32 10
*/
// 初始化A,B,C,D
A = 0x67452301;
B = 0xefcdab89;
C = 0x98badcfe;
D = 0x10325476;
- 按512位数据逐块处理输入信息
在我们进行在对数据块进行处理时,我们先来分析一下MD5算法的流程图。
- 在进行数据处理时,我们需要将当前的A,B,C,D获取,因为我们是在最终处理结束之后才更新A, B, C, D的值,所以我们需要提前获取,然后进行运算,最终再将变化后的内容更新到A,B,C,D
- 处理过程中要用一个含有64个元素的表K[1…64],表中的元素值由sin函数构建,K[i]等于2^(32) * abs(sin(i))的整数部分。
- 在chunk数组中我们使用的是g进行数组访问。g与 i 也存在一定的关系,即:
i g 0 <= i < 16 g = i; 16 <= i < 32 g = (5 * i + 1) % 16 32 <= i < 48 g = (3 * i + 5) % 16 48 <= i < 63 g = (7 * i) % 16
- 计算完成之后左移操作,左移的位数要使用s[ i ]数组进行移位。s[ i ]数组也是规定好的,即:
size_t s[ ]= { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7,12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10,15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 };- 重复操作,将最后所计算的a,b,c,d的值更新到A,B,C,D上,这样一块数据的处理结束,进行下一块数据的处理
- F,G,H,I函数的具体操作。
F(A, B, C) = (A & B) | ((~A) & C)
G(A, B, C) = (A & C) | ( B & (~C))
H(A, B, C) = A ^ B ^ C
I(A, B, C) = B ^ (A | (~C))
- 摘要输出
这一步拼接4个buffer(A,B,C,D)中的摘要信息,以A中的低位字节开始,D的高位字节结束。最终的输出是128bit摘要信息的16进制表示,故最后输出一个32长度的摘要信息。
4.4 保存磁盘中的文件信息
首先我们创建一set用来存磁盘中所有放文件的路径,因为我们需要计算每个文件的MD5码,所有我们需要一个map来存放关于文件向MD5映射的关系,最终,为了便于我们去删除MD5值相同的文件,我们也同样需要一个map 来存放MD5向文件映射的关系,但是在这里MD5码值会重复出现,容器的k值会重复出现,所以我们需要使用的是multimap。
4.5 删除MD5值相同的文件
五、 总结
在这个工具实现的过程中,让我学会了如何对一个项目去逐步实现,在面对一个复杂问题时,我们可以将这个复杂的问题分为若干的子问题,通过子问题的逐个解决,最终解决这一复杂问题。在这个项目中,接触到了一个新的知识——文件签名,了解到了如何去区分一个文件内容是否相同,并实现了MD5这一算法,在数据的存储中使用关联式容器,使得自己对于关联式容量的理解使用更加深刻。
程序原码链接: https://github.com/arryHZZ/CPlusPlus/tree/master/fileManger