为什么Python计算的"hashlib.sha1"与"git hash-object"对一个文件结果不同?

46 投票
2 回答
31223 浏览
提问于 2025-04-15 16:48

我正在尝试计算一个文件的SHA-1值。

我写了这个脚本:

def hashfile(filepath):
    sha1 = hashlib.sha1()
    f = open(filepath, 'rb')
    try:
        sha1.update(f.read())
    finally:
        f.close()
    return sha1.hexdigest()

对于一个特定的文件,我得到了这个哈希值:
8c3e109ff260f7b11087974ef7bcdbdc69a0a3b9
但是当我用git的hash_object命令计算这个值时,我得到了这个值:d339346ca154f6ed9e92205c3c5c38112e761eb7

为什么这两个值会不同呢?我是不是做错了什么,还是说我可以忽略这个差异?

2 个回答

32

作为参考,这里有一个更简洁的版本:

def sha1OfFile(filepath):
    import hashlib
    with open(filepath, 'rb') as f:
        return hashlib.sha1(f.read()).hexdigest()

再想想:虽然我从来没有见过,但我觉得 f.read() 可能会返回不完整的文件内容,或者在处理一个好几GB的文件时,f.read() 可能会导致内存不足。为了大家的理解,我们来看看怎么解决这个问题:第一个解决办法是:

def sha1OfFile(filepath):
    import hashlib
    sha = hashlib.sha1()
    with open(filepath, 'rb') as f:
        for line in f:
            sha.update(line)
        return sha.hexdigest()

不过,文件里不一定会有 '\n',所以 for 循环给我们的文件块如果以 '\n' 结尾,可能会让我们遇到最初的问题。可惜的是,我没有找到类似 Python 风格的方法来尽可能大块地遍历文件,这意味着我们可能得用 while True: ... break 循环,并且需要一个神秘的数字来表示块的大小:

def sha1OfFile(filepath):
    import hashlib
    sha = hashlib.sha1()
    with open(filepath, 'rb') as f:
        while True:
            block = f.read(2**10) # Magic number: one-megabyte blocks.
            if not block: break
            sha.update(block)
        return sha.hexdigest()

当然,谁能说我们能存储一兆字节的字符串呢?我们可能可以,但如果我们在一个小型嵌入式计算机上呢?

我希望能想到一种更简洁的方法,确保在处理超大文件时不会耗尽内存,而且没有神秘数字,同时性能也能和原来的简单 Python 解决方案一样好。

52

git 计算哈希值的方式是这样的:

sha1("blob " + filesize + "\0" + data)

参考链接

撰写回答