删除目录下相同文件的python代码(逐级优化)


Posted in Python onMay 25, 2012

这两天闲来无事在百度上淘了点图片,不多,也就几万张吧,其中有不少美女图片奥!哈哈!这里暂且不说图片是怎么获得的,咱聊聊得到图片以后发生的事。
遇到的第一个问题就是有些图片没有后缀名。在windows下,没有后缀名的文件是不能正确被识别的,没有预览,打开时还要选择打开方式,费劲!这个问题比较容易解决,给每个图片加上后缀名就是了。没有后缀名的图片也不多,不到1000张吧,一张一张地改很麻烦,还好我是学计算机的,上午写了个程序批量修改https://3water.com/article/30400.htm。这个问题就算解决了。接下来又遇到了一个新问题:图片多了,难免出现重复的,有些图片完全一样,没有必要都留着,我就想把所有的重复图片都删除。

让我们来分析一下这个问题:首先,文件个数非常多,手工查找是不现实的,再说,单凭我们肉眼,在几千张图片里面找到完全相同的难度也是很大的。如果不是图片而是其他文档,在不能预览的情况下要正确区分是很困难的。所以要用程序实现。那么用程序怎么实现呢?根据什么判断两个文件完全相同呢?首先,根据文件名判断是靠不住的,因为文件名可以被随意更改,但文件内容不变。再说在同一个文件夹下面,也不可能出现两个完全相同的文件名,操作系统不允许的。还有一种方法就是根据文件大小来判断,这不失为一种好办法,但是,文件大小相同的图片可能不一样。再说图片一般都比较小,超过3M的基本没有,大部分不够1M,如果文件夹下面文件特别多,出现大小相同的的文件可能性是相当大的。所以单凭文件大小来比较不靠谱。还有一种方法是读取每张图片的内容,然后比较这个图片的内容和其他图片是否完全相同,如果内容相同那么这两张图片肯定是完全相同的。这种方法看起来是比较完美的,让我们来分析一下他的时空效率:首先每张图片的内容都要和其他图片进行比较,这就是一个二重循环,读取的效率低,比较的效率更低,所有的都比较下来是非常费时的!内存方面,如果预先把所有图片读取到内存可以加快文件的比较效率,但是普通计算机的内存资源有限,如果图片非常多,好几个G的话,都读到内存是不现实的。如果不把所有的文件读取到内存,那么每比较一次之前就要先读取文件内容,比较几次就要读取几次,从硬盘读取数据是比较慢的,这样做显然不合适。那么有没有更好的方法呢?我冥思苦想,绞尽脑汁,最后想到了md5。md5是什么?你不知道吗?额,你火星了,抓紧时间duckduckgo吧!也许你会问,md5不是加密的吗?和我们的问题有关系吗?问得好!md5可以把任意长度的字符串进行加密后形成一个32的字符序列,包括数字和字母(大写或小写),因为字符串任何微小的变动都会导致md5序列改变,因此md5可以看作一个字符串的‘指纹'或者‘信息摘要',因为md5字符串总共有3632个,所以两个不同的字符串得到一个相同的md5概率是很小的,几乎为0,同样的道理,我们可以得到每个文件的md5,若干文件的md5相同的话就基本上可以肯定两个文件是相同的,因为md5相同而文件不同的概率太小了,基本可以忽略,这样我们就可以这样做:得到每个文件的md5,通过比较md5是否相同我们就可以确定两张图片是否相同。下面是代码实现,python的

# -*- coding: cp936 -*- 
import md5 
import os 
from time import clock as now 
def getmd5(filename): 
file_txt = open(filename,'rb').read() 
m = md5.new(file_txt) 
return m.hexdigest() 
def main(): 
path = raw_input("path: ") 
all_md5=[] 
total_file=0 
total_delete=0 
start=now() 
for file in os.listdir(path): 
total_file += 1; 
real_path=os.path.join(path,file) 
if os.path.isfile(real_path) == True: 
filemd5=getmd5(real_path) 
if filemd5 in all_md5: 
total_delete += 1 
print '删除',file 
else: 
all_md5.append(filemd5) 
end = now() 
time_last = end - start 
print '文件总数:',total_file 
print '删除个数:',total_delete 
print '耗时:',time_last,'秒' 
if __name__=='__main__': 
main()

上面的程序原理很简单,就是依次读取每个文件,计算md5,如果md5在md5列表不存在,就把这个md5加到md5列表里面去,如果存在的话,我们就认为这个md5对应的文件已经出现过,这个图片就是多余的,然后我们就可以把这个图片删除了。下面是程序的运行截图:
删除目录下相同文件的python代码(逐级优化)
我们可以看到,在这个文件夹下面有8674个文件,有31个是重复的,找到所有重复文件共耗时155.5秒。效率不算高,能不能进行优化呢?我分析了一下,我的程序里面有两个功能比较耗时间,一个是计算每个文件的md5,这个占了大部分时间,还有就是在列表中查找md5是否存在,也比较费时间的。从这两方面入手,我们可以进一步优化。

首先我想的是解决查找问题,或许我们可以对列表中的元素先排一下序,然后再去查找,但是列表是变化的,每次都排序的话效率就比较低了。我想的是利用字典进行优化。字典最显著的特点是一个key对应一个值我们可以把md5作为key,key对应的值就不需要了,在变化的情况下字典的查找效率比序列效率高,因为序列是无序的,而字典是有序的,查找起来当然更快。这样我们只要判断md5值是否在所有的key中就可以了。下面是改进后的代码:

# -*- coding: cp936 -*- 
import md5 
import os 
from time import clock as now 
def getmd5(filename): 
file_txt = open(filename,'rb').read() 
m = md5.new(file_txt) 
return m.hexdigest() 
def main(): 
path = raw_input("path: ") 
all_md5={} 
total_file=0 
total_delete=0 
start=now() 
for file in os.listdir(path): 
total_file += 1; 
real_path=os.path.join(path,file) 
if os.path.isfile(real_path) == True: 
filemd5=getmd5(real_path) 
if filemd5 in all_md5.keys(): 
total_delete += 1 
print '删除',file 
else: 
all_md5[filemd5]='' 
end = now() 
time_last = end - start 
print '文件总数:',total_file 
print '删除个数:',total_delete 
print '耗时:',time_last,'秒' if __name__=='__main__': 
main()

删除目录下相同文件的python代码(逐级优化)
从时间上看,确实比原来快了一点,但是还不理想。下面还要进行优化。还有什么可以优化呢?md5!上面的程序,每个文件都要计算md5,非常费时间,是不是每个文件都需要计算md5呢?能不能想办法减少md5的计算次数呢?我想到了一种方法:上面分析时我们提到,可以通过比较文件大小的方式来判断图片是否完全相同,速度快,但是这种方法是不准确的,md5是准确的,我们能不能把两者结合一下?答案是肯定的。我们可以认定:如果两个文件完全相同,那么这两个文件的大小和md5一定相同,如果两个文件的大小不同,那么这两个文件肯定不同!这样的话,我们只需要先查看文件的大小是否存在在size字典中,如果不存在,就将它加入到size字典中,如果大小存在的话,这说明有至少两张图片大小相同,那么我们只要计算文件大小相同的文件的md5,如果md5相同,那么这两个文件肯定完全一样,我们可以删除,如果md5不同,我们把它加到列表里面,避免重复计算md5.具体代码实现如下:
# -*- coding: cp936 -*- 
import md5 
import os 
from time import clock as now 
def getmd5(filename): 
file_txt = open(filename,'rb').read() 
m = md5.new(file_txt) 
return m.hexdigest() 
def main(): 
path = raw_input("path: ") 
all_md5 = {} 
all_size = {} 
total_file=0 
total_delete=0 
start=now() 
for file in os.listdir(path): 
total_file += 1 
real_path=os.path.join(path,file) 
if os.path.isfile(real_path) == True: 
size = os.stat(real_path).st_size 
name_and_md5=[real_path,''] 
if size in all_size.keys(): 
new_md5 = getmd5(real_path) 
if all_size[size][1]=='': 
all_size[size][1]=getmd5(all_size[size][0]) 
if new_md5 in all_size[size]: 
print '删除',file 
total_delete += 1 
else: 
all_size[size].append(new_md5) 
else: 
all_size[size]=name_and_md5 
end = now() 
time_last = end - start 
print '文件总数:',total_file 
print '删除个数:',total_delete 
print '耗时:',time_last,'秒' if __name__=='__main__': 
main()

时间效率怎样呢?看下图:

 删除目录下相同文件的python代码(逐级优化)

 只用了7.28秒!比前两个效率提高了十几倍!这个时间还可以接受

算法是个很神奇的东西,不经意间用一下会有意想不到的收获!上面的代码还可以进一步优化,比如改进查找算法等,读者有啥想法可以和我交流一下。换成C语言来实现可能会更快。呵呵,我喜欢python的简洁!
博主ma6174

Python 相关文章推荐
python如何实现远程控制电脑(结合微信)
Dec 21 Python
windows下python之mysqldb模块安装方法
Sep 07 Python
Python基于最小二乘法实现曲线拟合示例
Jun 14 Python
Python2.7环境Flask框架安装简明教程【已测试】
Jul 13 Python
python实现简单的单变量线性回归方法
Nov 08 Python
python实现五子棋游戏
Jun 18 Python
Python数据可视化实现正态分布(高斯分布)
Aug 21 Python
从多个tfrecord文件中无限读取文件的例子
Feb 17 Python
Win 10下Anaconda虚拟环境的教程
May 18 Python
Python定时任务框架APScheduler原理及常用代码
Oct 05 Python
如何使用python-opencv批量生成带噪点噪线的数字验证码
Dec 21 Python
基于Python编写简易版的天天跑酷游戏的示例代码
Mar 23 Python
ssh批量登录并执行命令的python实现代码
May 25 #Python
巧用Python装饰器 免去调用父类构造函数的麻烦
May 18 #Python
Python使用Socket(Https)Post登录百度的实现代码
May 18 #Python
写了个监控nginx进程的Python脚本
May 10 #Python
400多行Python代码实现了一个FTP服务器
May 10 #Python
使用PYTHON接收多播数据的代码
Mar 01 #Python
使用PYTHON创建XML文档
Mar 01 #Python
You might like
加强版phplib的DB类
2008/03/31 PHP
如何使用Strace调试工具
2013/06/03 PHP
PHP创建word文档的方法(平台无关)
2016/03/29 PHP
PHP面向对象之里氏替换原则简单示例
2018/04/08 PHP
解决在laravel中leftjoin带条件查询没有返回右表为NULL的问题
2019/10/15 PHP
$()JS小技巧
2007/07/21 Javascript
jQuery 隔行换色 支持键盘上下键,按Enter选定值
2009/08/02 Javascript
jquery实现下拉菜单的二级联动利用json对象从DB取值显示联动
2014/03/27 Javascript
javascript实现百度地图鼠标滑动事件显示、隐藏
2015/04/02 Javascript
详解JavaScript中的every()方法
2015/06/08 Javascript
基于JavaScript实现回到页面顶部动画代码
2016/05/24 Javascript
BootStrap实现手机端轮播图左右滑动事件
2016/10/13 Javascript
String字符串截取的四种方式总结
2016/11/28 Javascript
vue2.0结合Element实现select动态控制input禁用实例
2017/05/12 Javascript
使用RN Animated做一个“添加购物车”动画的方法
2018/09/12 Javascript
javascript中数组的常用算法深入分析
2019/03/12 Javascript
详解babel升级到7.X采坑总结
2019/05/12 Javascript
Vue scrollBehavior 滚动行为实现后退页面显示在上次浏览的位置
2019/05/27 Javascript
JavaScript命名空间模式实例详解
2019/06/20 Javascript
es6 super关键字的理解与应用实例分析
2020/02/15 Javascript
[01:06]DOTA2亚洲邀请赛专属珍藏-荧煌之礼
2017/03/24 DOTA
Python实现微信公众平台自定义菜单实例
2015/03/20 Python
Python matplotlib画图实例之绘制拥有彩条的图表
2017/12/28 Python
Python实现正整数分解质因数操作示例
2018/08/01 Python
Python 保存矩阵为Excel的实现方法
2019/01/28 Python
python opencv 二值化 计算白色像素点的实例
2019/07/03 Python
css 省略号 css3让多余的字符串消失并附加省略号的实现代码
2013/02/07 HTML / CSS
HTML5自定义data-* data(obj)属性和jquery的data()方法的使用
2012/12/13 HTML / CSS
html5+css如何实现中间大两头小的轮播效果
2018/12/06 HTML / CSS
英国剑桥包官网:The Cambridge Satchel Company
2016/08/01 全球购物
伦敦平价潮流珠宝首饰品牌:Astrid & Miyu
2016/10/10 全球购物
餐饮业员工工作决心书
2014/03/11 职场文书
《鸿门宴》教学反思
2014/04/22 职场文书
政治思想表现评语
2014/05/04 职场文书
5种方法告诉你如何使JavaScript 代码库更干净
2021/09/15 Javascript
Python测试框架pytest高阶用法全面详解
2022/06/01 Python