Python文件去重初尝试

情景

        MD5是一种常见的方法来进行文件去重,因为它可以为每个文件生成一个唯一的哈希值。

        但是,使用MD5来对大量的大文件进行去重可能会非常耗时。这是因为MD5需要读取整个文件内容来计算哈希值。如果文件非常大或者文件数量非常多,这个过程可能会非常慢。

        我这里想到的方法是结合文件大小(一个文件多少KB)和文件的名称相似度来去重文件,这样的性能消耗和时间消耗应该会少很多。

        获取文件大小和文件名称通常比计算文件的MD5哈希值要快得多。这是因为获取文件大小和名称只需要读取文件系统的元数据,而不需要读取整个文件的内容。这种操作通常可以在常数时间内完成,即它的时间复杂度是O(1)。

        相比之下,计算文件的MD5哈希值需要读取整个文件的内容,所以它的时间复杂度是O(n),其中n是文件的大小。对于大文件,这可能需要花费很长时间。

        以上是我使用这种方法的依据。

1. 需要使用的库

import os
import difflib

2. 针对某个目录,得到所有的文件名以及文件大小,形成一个元组

        元组的第一个元素是文件名,元组的第二个元素是文件大小

def list_files(directory):
    # 使用os.listdir遍历指定目录下的所有文件,并获取每个文件的大小
    files = [(filename, os.path.getsize(os.path.join(directory, filename))) for filename in os.listdir(directory)]
    # 列表生成式
    # 列表推导式
    # 首先是遍历某个路径下的顶级目录下的所有文件和文件夹,并且获得其名称
    # 然后根据每个文件的路径得到文件夹的名称和文件夹的大小,生产一个元组
    # 元组的第一个元素是文件名,第二个元素是文件的大小
    return files

3. 比较两个文件之间的文件名称差异

        这里返回的是相似的百分比,(最终的结果是0 - 1 之间)

        值越大,说明越相似

def similar(a, b):
    return difflib.SequenceMatcher(None, a, b).ratio()
    # difflib 是 Python 的一个内置库,它的名字是 "difference library" 的缩写,
    # 意思是 "差异库"。这个库提供了一些类和函数,
    # 用于比较序列的差异,包括字符串、列表等。
    # 它叫做序列匹配器
    # None这个位置的参数是isjunk, 是一个可选参数,
    # 如果为None,说明比较的过程中,任何细节都不能忽略
    # 如果想输入参数,这个参数需要是一个函数def isjunk(x):return x == ' '
    # 意思是,在比较的过程中,无视空格符,空格的差异不计入差异,

4. 根据文件大小来分组文件,相同文件夹大小的文件处于同一个群组(列表)

        这个数据的结构是列表里面嵌套列表。

        外层列表表示的是,这个列表里面有多个不同的群组。

        内层列表表示的是,这个列表里面都是相同文件大小的文件。

def group_files(filenames):
    groups = []

    for file in files:
        # 首先是遍历这个列表
        # 这个列表的元素是元组
        # 元组的第一个元素是文件名,第二个元素是文件大小
        filename, size = file
        # 解析(解包)这个元组,分别得到文件名称以及文件大小
        added = False
        # 初始化added标识符

        for group in groups:
            # 如果groups中有元素,那么就开始遍历
            _, group_size = group[0]
            # groups列表中的每一个元素是一个元组
            # 该元组的第一个元素是文件的名称,第二个元素是文件的大小
            # 在这里,文件夹的名称并不重要,重要的文件的大小
            # 因为这个函数主要是根据文件夹的大小来给文件分组
            # 相同大小的文件会被分到一个组中,不同文件大小的文件分别在各自的组别中
            if abs(size - group_size) / group_size < 0.01:
                group.append(file)
                added = True
                # 如果某个文件夹的大小等于某一个组,那么他就会被归属于某一个组
                break

        if not added:
            # 如果该文件的大小是独一无二的,就会成立一个新的组
            groups.append([file])

    # 最终得到不同文件大小的组别
    return groups

5. 删除重复的文件

        如果一批文件在同一个列表中(同一个群组),那么这一批文件可能是完全相同的文件,只需要保留一份即可。

        但是还不能这么快下定论,还需要再次把关,排除掉这种状况:虽然两个文件的大小相同,但是实际内容不同。

        第二次把关的是文件的名称,如果这两个文件的大小相同,名称也相似,那么它极大极大极大的概率就是重复文件,只留一个即可。

def delete_duplicates(groups, directory):
    # 在前面的步骤,我们把相同大小的文件都归类到一个组中
    # 如果这个文件的大小相同,文件名也想通,
    # 那么这两个文件极大极大的概率就是重复文件
    # 那么我们就可以只保留一个,另外一个删除
    for group in groups:
        # 获取第一个文件的前缀
        first_prefix = os.path.splitext(group[0][0])[0]
        # os.path.splitext(group[0][0]):这个函数将输入的路径(在这里是 group[0][0])分割成两部分,
        # 返回一个元组。
        # 元组的第一个元素是路径的前缀部分(也就是文件名中去掉扩展名后的部分),第二个元素是扩展名。
        # os.path.splitext(group[0][0])[0]:这个表达式获取上述元组的第一个元素,也就是文件名的前缀部分。
        # first_prefix = os.path.splitext(group[0][0])[0]:这个语句将文件名的前缀部分赋值给 first_prefix 变量。

        # 保留第一个文件,删除其他的文件
        for filename, _ in group[1:]:
            # 取相同大小文件群组第一个以后的基于文件
            # 这里重要的是文件名,而不是文件的大小
            # (元组的第一个元素是文件名,第二个元素是文件大小)
            # 获取当前文件的前缀
            current_prefix = os.path.splitext(filename)[0]

            # 如果前缀的相似度超过55%,则删除文件
            # 其余的文件都与第一个文件相比,如果名称相似度到达55%以上,就是重复文件
            if similar(first_prefix, current_prefix) > 0.55:
                os.remove(os.path.join(directory, filename))
            # 否则,打印文件名
            else:
                print(filename)

6. 完整步骤:

if __name__ == '__main__':
    directory = "./AutomationProfile"
    files = list_files(directory)
    groups = group_files(files)
    delete_duplicates(groups, directory)
    groups = group_files(files)
    print(f"还剩下{len(groups)}个不重复文件")

以上就是我的文件去重初尝试。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CCSBRIDGE

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值