猛禽的编程艺术

编程是一门艺术

快速文件hash

最近打算把家里服务器上的文件理一下,想把重复的文件找出来,虽然我已经用了ZFS的dedup,实际占用空间并不会重复,但是还是觉得有必要理一下……

写个程序扫描一遍并不复杂,但是要判断文件是不是重复就比较麻烦,可靠的方法当然是做全文件HASH,但是对于T级的硬盘来说,这样效率太低了,所以写了一段小代码来做一个快速的HASH。

def get_filemd5(fullpath, filename):
    file_size = get_filesize(fullpath, filename)
    block_count = int((QUICK_HASH_SIZE + BUFSIZE / 2) / BUFSIZE
                      if QUICK_HASH_SIZE else file_size / BUFSIZE)
    block_size = file_size * 1.0 / block_count if QUICK_HASH_SIZE \
        else BUFSIZE
    m = hashlib.md5()
    with open(os.path.join(fullpath, filename), "rb") as f:
        for i in xrange(block_count):
            pos = int(i * block_size / MINSIZE) * MINSIZE
            f.seek(pos, 0)
            buf = f.read(BUFSIZE)
            m.update(buf)
        remaining = file_size - f.tell()
        if remaining > 0:
            if QUICK_HASH_SIZE and remaining > BUFSIZE:
                f.seek(file_size - BUFSIZE, 0)
                remaining = BUFSIZE
            buf = f.read(remaining)
            m.update(buf)
    return m.hexdigest()

HASH算法是直接用MD5,简单方便。其中定义了三个常量:QUICK_HASH_SIZE、BUFSIZE和MINSIZE。

其中MINSIZE定义了对齐的大小,BUFSIZE定义了HASH分块的大小,QUICK_HASH_SIZE则是定义了需要做快速HASH的文件大小阈值。

方法很简单:文件小于QUICK_HASH_SIZE,则做全文件HASH,大于QUICK_HASH_SIZE则按BUFSIZE大小间隔取样,总取样数据大小为QUICK_HASH_SIZE。

每个BUFSIZE块在文件中的分布为均匀的,但是每块又以MINSIZE对齐,另外,因为通常文件最经常被修改的部分是最后的部分,所以最后一块一定是与文件结尾对齐,以保证HASH部分包括文件的结尾。

当然,因为没有对全文件做HASH,所以没有被HASH到的部分如果有修改,是不会被发现的,但是对我的需求来说基本不会有这种情况,所以不要紧,但是这样可以让HASH速度大大加快,特别是对那些几个G的大文件(不许联想,我说的是操作系统的安装ISO镜像文件之类)。

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭