代码之家  ›  专栏  ›  技术社区  ›  Ikke

为什么python计算的“hashlib.sha1”与文件的“git hash object”不同?

  •  45
  • Ikke  · 技术社区  · 16 年前

    我正在计算文件的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_对象计算值时,我得到这个值: d339346ca154f6ed9e92205c3c5c38112e761eb7

    他们为什么不同?我是在做错事,还是我可以忽略其中的区别?

    2 回复  |  直到 10 年前
        1
  •  51
  •   Community Mohan Dere    9 年前

    Git计算散列值如下:

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

    Reference

        2
  •  32
  •   Ben    12 年前

    这里有一个更简洁的版本供参考:

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

    再想一想:虽然我从未见过,但我认为 f.read() 返回小于完整文件或多个千兆字节的文件,以便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” 会给我们带来和原来一样的问题。遗憾的是,我没有看到任何类似的pythonic方法来尽可能大地迭代文件块,我认为这意味着我们陷入了 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()
    

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

    我希望我能想出一个更干净的方法,保证不会在巨大的文件上耗尽内存,也不会有神奇的数字,它的性能和最初简单的蟒蛇解决方案一样好。