python删除重复文件代码

来源:互联网 发布:网络验证系统哪个好 编辑:程序博客网 时间:2024/05/11 03:59
整理磁盘才发现有好多重复文件,便想手写一个删除重复文件的工具。我当然可以用java轻松写一个了,只是最近看了一些python方面的东西,所以想不如拿python练练手。功能很简单,只是为了查阅资料,我花费了好长时间。

需求:查找一件夹中的文件,包括子目录中的文件,找出重名文件,然后比较大小,如果大小也相同,则删除。

方案:递归列出目录底下所有文件,并将同名文件合并到一起,用dict(java里是map)保存,键用文件名,而值用列表,保存文件名对应的路径。然后去除map值中len为1数项目。接着循环map,判断时间是否相同,相同则删除

下面是我的实现过程:

我打算写个递归函数,遍历目录底下的所有文件。这个函数参数除了要遍历的目录路径,还有一个函数参数,用来处理每次遍历的文件。如果是java里面,我当然会用策略模式:比如定义一个处理文件的接口,传给遍历目录的方法。java8里面的lambda表达式也使策略模式使用起来更加方便灵活。但python可以直接传方法作为参数,这样我们就不用定义接口了(事实上我更喜欢java用接口作参数。)

第一步遍历文件,并按文件名,进行分组操作,代码如下

import osimport os.path##处理文件def processFile(path, processFun):    '''    用来处理遍历文件    :param path: 要处理文件路径    :param processFun: 对某一文件处理的方法    :return:     '''    fileList = os.walk(path)    for dirpath, dirnames, filenames in fileList:        for mFname in filenames:            processFun(dirpath, mFname)rFdic = dict();def process1(dirpath, filename):            if (filename not in rFdic):        rFdic[filename] = [dirpath]    else:        rList = rFdic[filename]        rList.append(dirpath)processFile("/Users/wzp/tmp/file/", process1)for k1 in [k for k in rFdic if len(rFdic[k]) ==1]:    rFdic.pop(k1)print(rFdic)

processFile方法是我计划用来实逻辑的主体的方法。里面实现了对文件的遍历(我本想用递归的,但python里面有现成的方法)。此外遍历到每个文件时调用processFun方法(不好意,我没有做精心的方法命名)。这个函数是作为参数传进来的。这样可以实现我业务的分离。下面调用processFile且传递了相应的路径,将方法process1参数传了进去。process1主要实现了分组操作,代码很简单。但写完后,我发现了一个问题。那就是分组完成后,我希望对数据再进行一个处理,比如在调用processFile后,我用了一个for方法,将rFdic中的没有重复的数据去除掉。然而我并不想在方法外做,我想最后只用调用processFile便可完成所有操作。怎么办?当然是在方法上增加一个参数,这个参数也传递一个方法,这个方法在遍历文档结束后调用,用来处理分组后的数据。妈的,我突然想到,我的做法好像跟google的MapReduce相似。虽然我还没仔细研究过MapReduce,但MapReduce的思路好像也是类似。好吧,我添加一个参数,同时借用google的map和reduce方法名(虽然我原计划我processFile方法不局限于分组删除之类的,不过我一时取不出好的方法名),

版2 的代码如下:

import osimport os.pathdef processFile(path, filemap, filereduce):    fileList = os.walk(path)    for dirpath, dirnames, filenames in fileList:        for mFname in filenames:            filemap(dirpath, mFname)    filereduce()rFdic = dict();def map1(dirpath, filename):    #  print(dirpath + filename)    if (filename not in rFdic):        rFdic[filename] = [dirpath]    else:        rList = rFdic[filename]        rList.append(dirpath)def reduce1():    for k1 in [k for k in rFdic if len(rFdic[k]) == 1]:        rFdic.pop(k1)    for k1 in rFdic:        for mdir in rFdic[k1]:            mpath = mdir + "/" + k1            print(mpath + "\t|\t", os.path.getsize(mpath))def reduce2():    for k1 in [k for k in rFdic if len(rFdic[k]) == 1]:        rFdic.pop(k1)    for k1 in rFdic:        mfsize = -1        for mdir in rFdic[k1]:            mpath = mdir + "/" + k1            tfsize = os.path.getsize(mpath)            if (tfsize == mfsize):                print("del file"+mpath,tfsize)            mfsize = tfsize;processFile("/Users/wzp/tmp/file/", map1, reduce2)print(rFdic)


我们改变了 processFile的参数名,并且增加了一个参数。你可以看到我这里用的方法跟mapreduce是不一样的,mapreduce中用map输出的参数作为reduce输入的参数。我们这里没有这个必要,因为这两个方法可以共享一个变量,当然也可以把两个方法写到一个类中,这样就不用完全局变量了。此外,我们这里有个reduce1方法,和reduce2方法。他们可以实现不同的逻辑,在调用processFile时,想要什么样的算法,可以传相应的reduce进去。你可能注意到redduce1和reduce2有两段重复的代码。其实如果是java中,可以用组合模式。

版本3:

import osimport os.pathdef processFile(path, filemap, filereduce):    fileList = os.walk(path)    for dirpath, dirnames, filenames in fileList:        for mFname in filenames:            filemap(dirpath, mFname)    filereduce()rFdic = dict();def map1(dirpath, filename):    #  print(dirpath + filename)    if (filename not in rFdic):        rFdic[filename] = [dirpath]    else:        rList = rFdic[filename]        rList.append(dirpath)def reduce0():    for k1 in [k for k in rFdic if len(rFdic[k]) == 1]:        rFdic.pop(k1)def reduce1():    for k1 in rFdic:        for mdir in rFdic[k1]:            mpath = mdir + "/" + k1            print(mpath + "\t|\t", os.path.getsize(mpath))def reduce2():    for k1 in rFdic:        mfsize = -1        for mdir in rFdic[k1]:            mpath = mdir + "/" + k1            tfsize = os.path.getsize(mpath)            if (tfsize == mfsize):                os.remove(mpath)                print("deleted file :"+mpath)            mfsize = tfsize;def reduce3():    reduce0()    print("do you want delete these files?")    reduce1()    str = input("Enter Y or N ")    if str != "Y":       print("file not delete ")       return    print("start delte file")    reduce2()processFile("/Users/wzp/Downloads/xx", map1, reduce3)


我们没有改变我们processFile方法,只是添加两个reduce方法,在方法reduce3里面,我们调用了其它几个reduce方法。ok完成。尽管功能已完成,但还是不如人意,于是我想到了版本5(你或许会问,版本4呢?4没贴出来,我想直接跳5)。首先我们是文件名与文件大小来判断是否是同一文件。于是我们在map方法中,就能按文件名与文件大小联合在一起分组。

def fileNameAndSizeMap(dirpath, filename):    mFilePath = dirpath + "/" + filename    mkey = mFilePath + "|" + str(os.path.getsize(mFilePath));    if (mkey not in rFdic):        rFdic[mkey] = [dirpath]    else:        rList = rFdic[mkey]        rList.append(filename)            processFile("/Users/wzp/Downloads/xx", fileNameAndSizeMap, lambda:print(rFdic))

其他方法不用改变,我们只需要添加两个方法即可,以上我添加map方法,用文件名与文件大小作为键,值改成了文件全路径,这样可以省去很多麻烦,但我没我写reduce了,引用lambda表达式,直接将rFdic中的值直接打印出来,以后再完善。困了,半睡眠状态写的