删除目录下相同文件的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实现数通设备端口监控示例
Apr 02 Python
放弃 Python 转向 Go语言有人给出了 9 大理由
Oct 20 Python
python matplotlib中文显示参数设置解析
Dec 15 Python
使用Python实现租车计费系统的两种方法
Sep 29 Python
Python 创建新文件时避免覆盖已有的同名文件的解决方法
Nov 16 Python
python 遍历列表提取下标和值的实例
Dec 25 Python
Python hexstring-list-str之间的转换方法
Jun 12 Python
PyCharm搭建Spark开发环境实现第一个pyspark程序
Jun 13 Python
pytorch:model.train和model.eval用法及区别详解
Feb 20 Python
python opencv 图像边框(填充)添加及图像混合的实现方法(末尾实现类似幻灯片渐变的效果)
Mar 09 Python
基于Python爬取股票数据过程详解
Oct 21 Python
python爬虫请求头的使用
Dec 01 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
PHP 5.0 Pear安装方法
2006/12/06 PHP
PHP中的session安全吗?
2016/01/22 PHP
php类的自动加载操作实例详解
2016/09/28 PHP
ThinkPHP实现转换数据库查询结果数据到对应类型的方法
2017/11/16 PHP
PHP的垃圾回收机制代码实例讲解
2021/02/27 PHP
jquery仿京东导航/仿淘宝商城左侧分类导航下拉菜单效果
2013/04/24 Javascript
固定背景实现的背景滚动特效示例分享
2013/05/19 Javascript
javascript中怎么做对象的类型判断
2013/11/11 Javascript
js中hash和ico的关联分析
2015/02/05 Javascript
JavaScript 中使用 Generator的方法
2017/12/29 Javascript
关于微信小程序获取小程序码并接受buffer流保存为图片的方法
2019/06/07 Javascript
node中实现删除目录的几种方法
2019/06/24 Javascript
解决vue语法会有延迟加载显现{{xxx}}的问题
2019/11/14 Javascript
利用JS如何获取form表单数据
2019/12/19 Javascript
js实现鼠标拖拽div左右滑动
2020/01/15 Javascript
element-ui中dialog弹窗关闭按钮失效的解决
2020/09/22 Javascript
[01:26]DOTA2荣耀之路2:iG,China
2018/05/24 DOTA
win7安装python生成随机数代码分享
2013/12/27 Python
详解字典树Trie结构及其Python代码实现
2016/06/03 Python
Python 闭包的使用方法
2017/09/07 Python
python爬虫 正则表达式使用技巧及爬取个人博客的实例讲解
2017/10/20 Python
pycharm安装和首次使用教程
2018/08/27 Python
Python中将两个或多个list合成一个list的方法小结
2019/05/12 Python
Jacobi迭代算法的Python实现详解
2019/06/29 Python
python 使用matplotlib 实现从文件中读取x,y坐标的可视化方法
2019/07/04 Python
使用python模拟命令行终端的示例
2019/08/13 Python
Python实现检测文件的MD5值来查找重复文件案例
2020/03/12 Python
Python unittest生成测试报告过程解析
2020/09/08 Python
python判断字符串以什么结尾的实例方法
2020/09/18 Python
python跨文件使用全局变量的实现
2020/11/17 Python
世界顶级足球门票网站:Live Football Tickets
2017/10/14 全球购物
.NET概念性的面试题
2012/02/29 面试题
信息技术培训感言
2014/03/06 职场文书
优秀毕业生求职信
2014/06/05 职场文书
中学生运动会新闻稿
2014/09/24 职场文书
地球上的星星观后感
2015/06/02 职场文书