• [Python] 通过md5去重 筛选文件代码


    这是一些代码记录

    • 这次是帮朋友恢复硬盘,扫描到的结果包含了好多个分区,通过将分区中的数据导出发现很多文件是重复的。所以想到通过python代码去重。
    • 首先把所有分区的图片文件都放到一个文件夹A中,如果命名有冲突,对于win10可以选"让我决定每个文件"然后把两个对勾都打上,这样对于重复的文件win10会给加上(1)这样的后缀(所以去重是把文件名比较长的移除)。通过下面的去重代码就能轻易移除掉重复的文件。
    • 因为朋友的硬盘上放了很多新的图片,所以通过第二个代码把A文件夹中存在于硬盘中的文件移除。
    • 然后,写完第二个代码之后想到了更好的思路以及进度条的实现,所以又写了一个验证的代码。

    去重

    通过获取md5去重,保留文件名比较短的版本(主要是去除命名中最后的(1)这样的标记) 为了保险起见 只是把重复的文件移动走了 而不是删除,代码出现错误也不至于有什么损失。强烈建议这么做

    import os
    import hashlib
    import shutil
    
    hash_dict = {}
    
    def get_md5(file_name):
        with open(file_name, "rb") as f:
            r = f.read()
            m = hashlib.md5()
            m.update(r)
            return m.hexdigest()
    
    A_file_list = os.listdir("A")
    
    t_num = len(A_file_list)
    print ("总文件数: " + str(t_num));
    cnt = 0
    for file in A_file_list:
        md5_str = get_md5('A/'+file)
        if md5_str in hash_dict:  # 重复文件
            print (f"重复: {hash_dict[md5_str]} | {file}");
            if len(file) < len(hash_dict[md5_str]):
                shutil.move('A/' + hash_dict[md5_str], 'B/' + hash_dict[md5_str])
                # os.remove('A/' + hash_dict[md5_str])
                hash_dict[md5_str] = file
            else:
                shutil.move('A/' + file, 'B/' + file)
                # os.remove('A/' + file)
        else:
            hash_dict[md5_str] = file
        cnt += 1
        print (f"{str(cnt)}/{str(t_num)}")
    
    print ("done.")
    

    递归查找文件比对筛选

    通过上一步去重,下面的代码先对A文件夹中的文件都取md5,然后遍历F盘的文件,并且计算md5,如果有相同的,那么移动到B文件夹。

    import os
    import hashlib
    import shutil
    
    hash_dict = {}
    f_file_cnt = 0
    
    def get_md5(file_name):
        with open(file_name, "rb") as f:
            r = f.read()
            m = hashlib.md5()
            m.update(r)
            return m.hexdigest()
            
    def fun(path):
        global f_file_cnt
        try:
            os.chdir(path)
        except:
            return
        file_list = os.listdir()
        for file in file_list:
            if os.path.isdir(file):
                fun(file)
            elif os.path.isfile(file):
                if os.path.getsize(file) > 73056832:
                    continue
                md5_str = get_md5(file)
                # with open("D://F.md5", 'a') as f:
                    # f.write(md5_str+"\n")
                if md5_str in hash_dict:
                    try:
                        shutil.move("D://恢复/A/"+hash_dict[md5_str], "D://恢复/B/"+hash_dict[md5_str])
                        print ("D://恢复/A/"+hash_dict[md5_str])
                    except Exception as e:
                        print(e)
                f_file_cnt += 1
                if (f_file_cnt % 50) == 0:
                    print(f"{(f_file_cnt/25524)*100}% - {md5_str}")
    
        os.chdir("..")
    
    A_file_list = os.listdir("A")
    
    t_num = len(A_file_list)
    
    print ("总文件数: " + str(t_num));
    
    cnt = 0
    
    #读取A文件夹的md5 与F盘现有文件对比 结果作为依据删除掉A文件夹的文件
    print("载入A文件夹md5")
    for file in A_file_list:
        md5_str = get_md5('A/'+file)
        hash_dict[md5_str] = file
        cnt += 1
        if (cnt % 50) == 0:
            print (f"{(cnt/t_num)*100}%")
    print("A文件夹md5读取完毕\n开始遍历F盘")
    
    
    fun("F:/")
    
    print (f_file_cnt)
    print ("done.")
    

    最终检查

    通过修改上面代码(参考注释的代码),可以先遍历一遍F盘的文件,得到文件的md5列表,然后再和A文件夹里面的文件按照下面的代码比对,能节省很多时间。原因是因为移动硬盘速度太慢了,我要是早点这么干,早就筛选出必要的文件了。。。

    import os
    import hashlib
    import shutil
    from tqdm import tqdm
    
    f_hash_list = []
    
    def get_md5(file_name):
        with open(file_name, "rb") as f:
            r = f.read()
            m = hashlib.md5()
            m.update(r)
            return m.hexdigest()
            
    
    with open("D://F.md5") as f:
        f_hash_list = f.readlines()
        
    file_list = os.listdir("B")
    
    pbar = tqdm(total=len(file_list))
    
    for file in file_list:
        md5_str = get_md5("B/"+file)
        if md5_str+"\n" in f_hash_list:
            try:
                shutil.move("B/"+file, "C/"+file)
            except Exception as e:
                print (e)
        pbar.update(1)
    
    pbar.close()
    print("done.")
    

    这里也用到了tqdm库来实现进度条的效果,感觉效果很好,只是这样的话,就不能随意输出东西了。否则会出现多个进度条,这个目前我不知道怎么解决问题,但是考虑到控制台输出东西会严重拖慢程序的运行速度,不输出也没什么关系。

  • 相关阅读:
    PIE-Basic 自定义滤波
    PIE-Basic 常用滤波
    PIE-Basic 中值滤波
    PIE-Basic 均值滤波
    PIE-Basic 小波变换
    PIE-Basic 傅里叶变换
    PIE-Basic 去相关拉伸
    .net调用数据库执行Mysql存储过程,提示 Procedure or function XXXX Cannot be found in database xxx
    C#几种类型转换方法的个人总结
    Winform无法改变窗体大小
  • 原文地址:https://www.cnblogs.com/cjdty/p/15850592.html
Copyright © 2020-2023  润新知