目录
  1. 关于MD5
  2. 文件MD5验证特性
  3. MD5的python实现
  4. 文件重复性检测
  5. 完整代码
【脚本】Python删除重复文件

关于MD5

  • md5的全称是md5信息摘要算法(英文:MD5 Message-Digest Algorithm ),一种被广泛使用的密码散列函数,可以产生一个128位(16字节,1字节8位)的散列值(常见的是用32位的16进制表示,比如:0caa3b23b8da53f9e4e041d95dc8fa2c),用于确保信息传输的完整一致。
  • md5将整个文件当做一个大文本信息,通过不可逆的字符串变换算法,产生一个唯一的MD5信息摘要。文件的md5类似于人的指纹,在世界上是独立无二的,如果任何人对文件做了任何改动,其md5的值也就是对应的“数字指纹”都会发生变化。

文件MD5验证特性

  1. md5码具有高度的散列性,没有规律可循,哪怕原信息只有一点点的变化,比如多个空格,那么就会导致md5发生巨大变化,也可以说产生的md5码是不可预测的。
  2. 任意长度的数据,算出的md5值得长度都是固定的。
  3. 拷贝(重复)的文件md5不会改变。

MD5的python实现

  • 需要引入模块 import hashlib 来实现MD5算法实现
  • 文件的MD5校验代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib

def md5(filename, block_size=65536):
"""
:param filename: 文件名
:param block_size:
:return: md5值
"""
hash_ = hashlib.md5()
with open(filename, "rb") as f:
for i in iter(lambda: f.read(block_size), b""):
hash_.update(i)
return hash_.hexdigest()
  • update() 用于将数据传入 hash_ 对象中,数据量大时,可分块传入;
  • hexdigest() 方法用于获取 hash_ 对象的16进制md5显示。

文件重复性检测

  • 使用 os.walk() 对目标文件夹进行树形遍历;
  • 分别对所有文件进行md5值计算。
  • 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
def get_duplicate(path):
total_files = [(f, os.path.join(root, f)) for root, _, files in os.walk(path) for f in files]
duplicate_tuple = []
unique_md5 = []
with tqdm(total_files, desc="正在扫描中") as bar:
for i in bar:
md5_values = md5(i[1]) # md5计算
if md5_values in unique_md5:
duplicate_tuple.append(i)
else:
unique_md5.append(md5_values)
bar.set_description(f"正在扫描中,发现{len(duplicate_tuple)}个重复文件。")
return duplicate_tuple

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import os
import hashlib
import shutil
from tqdm import tqdm

class DuplicateFile(object):
"""
这是一个对重复文件的处理类
"""

def __init__(self, path):
super().__init__()
self.path = path
self.duplicate_tuple = self._get_duplicate(path)

@property
def show(self):
"""
显示重复文件
"""
duplicate, _ = zip(*self.duplicate_tuple)
print("\n".join(duplicate))
print(f"\n重复文件共{len(duplicate)}个")
return None

@staticmethod
def md5(filename, block_size=65536):
"""
:param filename: 文件名
:param block_size:
:return: md5值
"""
hash_ = hashlib.md5()
with open(filename, "rb") as f:
for i in iter(lambda: f.read(block_size), b""):
hash_.update(i)
return hash_.hexdigest()

def _get_duplicate(self, path):
total_files = [(f, os.path.join(root, f)) for root, _, files in os.walk(path) for f in files]
duplicate_tuple = []
unique_md5 = []
with tqdm(total_files, desc="正在扫描中") as bar:
for i in bar:
md5_values = self.md5(i[1])
if md5_values in unique_md5:
duplicate_tuple.append(i)
else:
unique_md5.append(md5_values)
bar.set_description(f"正在扫描中,发现{len(duplicate_tuple)}个重复文件。")
return duplicate_tuple

def delete(self):
"""
删除重复文件
"""
if self.duplicate_tuple:
confirm = input("删除后无法恢复,是否继续(y/n)?")
if confirm in "Yy":
for file, path in self.duplicate_tuple:
print(f"已删除{file}")
os.remove(path)
print("删除完毕")
else:
print("已取消")
else:
print("没有重复文件")
return None

def move(self, new_path):
"""
剪切重复文件
"""
if self.duplicate_tuple:
if not os.path.exists(new_path):
print(f"{new_path} not exist,正在创建文件夹")
os.makedirs(new_path)
print("移动中")
for file, path in self.duplicate_tuple:
print(f"正在移动{file}")
dst = os.path.join(new_path, file)
shutil.move(path, dst)
print("移动完毕")
else:
print("没有重复文件")
return None

if __name__ == "__main__":
src_path="./"
dup=DuplicateFile(src_path)
dup.delete()
文章作者: Haibei
文章链接: http://www.haibei.online/posts/2694026283.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Haibei的博客
打赏
  • 微信
  • 支付宝

评论