删除目录下相同文件的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操作MySQL简单实现方法
Jan 26 Python
Python中使用MELIAE分析程序内存占用实例
Feb 18 Python
Python操作RabbitMQ服务器实现消息队列的路由功能
Jun 29 Python
Python字符串拼接的几种方法整理
Aug 02 Python
python中多个装饰器的执行顺序详解
Oct 08 Python
Python快速转换numpy数组中Nan和Inf的方法实例说明
Feb 21 Python
python向图片里添加文字
Nov 26 Python
如何基于Python + requests实现发送HTTP请求
Jan 13 Python
python实现从ftp上下载文件的实例方法
Jul 19 Python
python 贪心算法的实现
Sep 18 Python
python爬取音频下载的示例代码
Oct 19 Python
pandas进行数据输入和输出的方法详解
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
apache+php+mysql安装配置方法小结
2010/08/01 PHP
PHP计算一年多少个星期和每周的开始和结束日期
2014/07/01 PHP
php中mkdir函数用法实例分析
2014/11/15 PHP
php验证码实现代码(3种)
2015/09/07 PHP
PHP版单点登陆实现方案的实例
2016/11/17 PHP
33种Javascript 表格排序控件收集
2009/12/03 Javascript
jquery实现鼠标拖拽滑动效果来选择数字的方法
2015/05/04 Javascript
js+canvas绘制矩形的方法
2016/01/28 Javascript
原生js实现百叶窗效果及原理介绍
2016/04/12 Javascript
jQuery实现点击查看大图并以弹框的形式居中
2016/08/08 Javascript
JavaScript 中 apply 、call 的详解
2017/03/21 Javascript
jQuery插件FusionCharts绘制2D环饼图效果示例【附demo源码】
2017/04/10 jQuery
关于react中组件通信的几种方式详解
2017/12/10 Javascript
Vue动画事件详解及过渡动画实例
2019/02/09 Javascript
vue 实现购物车总价计算
2019/11/06 Javascript
JS实现动态倒计时功能(天数、时、分、秒)
2019/12/12 Javascript
解决Vue @submit 提交后不刷新页面问题
2020/07/18 Javascript
Vue中正确使用Element-UI组件的方法实例
2020/10/13 Javascript
antd form表单数据回显操作
2020/11/02 Javascript
Python入门之modf()方法的使用
2015/05/15 Python
详解Python装饰器
2019/03/25 Python
Django Admin设置应用程序及模型顺序方法详解
2020/04/01 Python
python 写一个文件分发小程序
2020/12/05 Python
html5实现输入框fixed定位在屏幕最底部兼容性
2020/07/03 HTML / CSS
Old Navy加拿大官网:美式休闲服饰品牌
2017/09/26 全球购物
英国复古和经典球衣网站:Vintage Football Shirts
2018/10/05 全球购物
Lucene推荐的分页方式是什么?
2015/12/07 面试题
幼儿园运动会入场词
2014/02/10 职场文书
《匆匆》教学反思
2014/02/22 职场文书
学校安全防火方案
2014/06/07 职场文书
四风批评与自我批评范文
2014/10/14 职场文书
幼儿园中班教师个人工作总结
2015/02/06 职场文书
父亲节活动总结
2015/02/12 职场文书
技能培训通讯稿
2015/07/18 职场文书
Python人工智能之混合高斯模型运动目标检测详解分析
2021/11/07 Python
hive数据仓库新增字段方法
2022/06/25 数据库