【问题标题】:compute crc of file in python在python中计算文件的crc
【发布时间】:2010-12-17 02:30:24
【问题描述】:

我想计算文件的CRC 并得到如下输出:E45A12AC。这是我的代码:

#!/usr/bin/env python 
import os, sys
import zlib

def crc(fileName):
    fd = open(fileName,"rb")
    content = fd.readlines()
    fd.close()
    for eachLine in content:
        zlib.crc32(eachLine)

for eachFile in sys.argv[1:]:
    crc(eachFile)

这会计算每一行的 CRC,但它的输出(例如 -1767935985)不是我想要的。

Hashlib 按我想要的方式工作,但它会计算 md5:

import hashlib
m = hashlib.md5()
for line in open('data.txt', 'rb'):
    m.update(line)
print m.hexdigest()

是否有可能使用zlib.crc32 获得类似的东西?

【问题讨论】:

    标签: python hash crc


    【解决方案1】:

    更紧凑和优化的代码

    def crc(fileName):
        prev = 0
        for eachLine in open(fileName,"rb"):
            prev = zlib.crc32(eachLine, prev)
        return "%X"%(prev & 0xFFFFFFFF)
    

    PS2:旧 PS 已被弃用 - 因此已删除 - 因为评论中的建议。谢谢你。我不明白,我怎么错过了这个,但它真的很棒。

    【讨论】:

    • 如果您将prev 设置为 0,则无需担心异常。
    • 更快的东西会产生相同的输出: def crc(filename): return "%X"%(zlib.crc32(open(filename,"rb").read()) & 0xFFFFFFFF) 这会将整个文件读入内存并计算 CRC32。当然,文件越大,程序需要的内存就越多;取决于你想要的权衡,内存换速度,还是速度换内存。
    • 在保持低内存使用率的同时显着加快计算速度(因子 2--3)的一种方法是读取固定大小的块,而不是从二进制文件中读取“行”。为此添加了单独的答案。
    【解决方案2】:

    kobor42 答案的修改版本,通过读取固定大小的块而不是“行”,性能提高了 2-3 倍:

    import zlib
    
    def crc32(fileName):
        with open(fileName, 'rb') as fh:
            hash = 0
            while True:
                s = fh.read(65536)
                if not s:
                    break
                hash = zlib.crc32(s, hash)
            return "%08X" % (hash & 0xFFFFFFFF)
    

    在返回的字符串中还包括前导零。

    【讨论】:

      【解决方案3】:

      hashlib - CRC-32 支持的兼容接口:

      导入zlib
      
      crc32 类(对象):
          名称 = 'crc32'
          摘要大小 = 4
          块大小 = 1
      
          def __init__(self, arg=''):
              self.__digest = 0
              自我更新(arg)
      
          定义副本(自我):
              复制=超级(self.__class__,self).__new__(self.__class__)
              复制.__digest = self.__digest
              返回副本
      
          def 摘要(自我):
              返回自我.__摘要
      
          def hexdigest(自我):
              return '{:08x}'.format(self.__digest)
      
          定义更新(自我,arg):
              self.__digest = zlib.crc32(arg, self.__digest) & 0xffffffff
      
      # 现在你可以定义 hashlib.crc32 = crc32
      导入哈希库
      hashlib.crc32 = crc32
      
      # Python > 2.7: hashlib.algorithms += ('crc32',)
      # Python > 3.2: hashlib.algorithms_available.add('crc32')

      【讨论】:

        【解决方案4】:

        要将任何整数的最低 32 位显示为 8 个不带符号的十六进制数字,您可以按位“屏蔽”该值,并使用由 32 位组成的掩码将其全部设为值 1,然后应用格式设置。即:

        >>> x = -1767935985
        >>> format(x & 0xFFFFFFFF, '08x')
        '969f700f'
        

        您格式化的整数是来自zlib.crc32 还是来自任何其他计算,这完全无关紧要。

        【讨论】:

        • 好点重新:格式化,但看起来他的代码也没有计算出他想要的。这里确实有两个问题:1)计算文件的CRC。 2) 将 CRC 值显示为十六进制。
        • 不仅如此,而且格式比 "%X"%(x & 0xFFFFFFFF) 慢,提供了 kobor24 的答案。但很高兴看到另一种方法,我以前从未使用过格式。
        【解决方案5】:

        Python 3.8+(使用海象运算符):

        import zlib
        
        def crc32(filename, chunksize=65536):
            """Compute the CRC-32 checksum of the contents of the given filename"""
            with open(filename, "rb") as f:
                checksum = 0
                while (chunk := f.read(chunksize)) :
                    checksum = zlib.crc32(chunk, checksum)
                return checksum
        

        chunksize 是您一次读取文件的字节数。不管你设置什么,你都会为同一个文件获得相同的哈希值(设置得太低可能会使你的代码变慢,太高可能会占用太多内存)。

        结果是一个 32 位整数。空文件的 CRC-32 校验和为0

        【讨论】:

          【解决方案6】:

          编辑后在下方包含 Altren 的解决方案。

          CrouZ 答案的修改和更紧凑的版本,性能略有提高,使用 for 循环和文件缓冲:

          def forLoopCrc(fpath):
              """With for loop and buffer."""
              crc = 0
              with open(fpath, 'rb', 65536) as ins:
                  for x in range(int((os.stat(fpath).st_size / 65536)) + 1):
                      crc = zlib.crc32(ins.read(65536), crc)
              return '%08X' % (crc & 0xFFFFFFFF)
          

          结果,在 6700k 硬盘中:

          (注意:经过多次重新测试,它始终更快。)

          Warming up the machine...
          Finished.
          
          Beginning tests...
          File size: 90288KB
          Test cycles: 500
          
          With for loop and buffer.
          Result 45.24728019630359 
          
          CrouZ solution
          Result 45.433838356097894 
          
          kobor42 solution
          Result 104.16215688703986 
          
          Altren solution
          Result 101.7247863946586  
          

          使用以下脚本在 Python 3.6.4 x64 中测试:

          import os, timeit, zlib, random, binascii
          
          def forLoopCrc(fpath):
              """With for loop and buffer."""
              crc = 0
              with open(fpath, 'rb', 65536) as ins:
                  for x in range(int((os.stat(fpath).st_size / 65536)) + 1):
                      crc = zlib.crc32(ins.read(65536), crc)
              return '%08X' % (crc & 0xFFFFFFFF)
          
          def crc32(fileName):
              """CrouZ solution"""
              with open(fileName, 'rb') as fh:
                  hash = 0
                  while True:
                      s = fh.read(65536)
                      if not s:
                          break
                      hash = zlib.crc32(s, hash)
                  return "%08X" % (hash & 0xFFFFFFFF)
          
          def crc(fileName):
              """kobor42 solution"""
              prev = 0
              for eachLine in open(fileName,"rb"):
                  prev = zlib.crc32(eachLine, prev)
              return "%X"%(prev & 0xFFFFFFFF)
          
          def crc32altren(filename):
              """Altren solution"""
              buf = open(filename,'rb').read()
              hash = binascii.crc32(buf) & 0xFFFFFFFF
              return "%08X" % hash
          
          fpath = r'D:\test\test.dat'
          tests = {forLoopCrc: 'With for loop and buffer.', 
               crc32: 'CrouZ solution', crc: 'kobor42 solution',
                   crc32altren: 'Altren solution'}
          count = 500
          
          # CPU, HDD warmup
          randomItm = [x for x in tests.keys()]
          random.shuffle(randomItm)
          print('\nWarming up the machine...')
          for c in range(count):
              randomItm[0](fpath)
          print('Finished.\n')
          
          # Begin test
          print('Beginning tests...\nFile size: %dKB\nTest cycles: %d\n' % (
              os.stat(fpath).st_size/1024, count))
          for x in tests:
              print(tests[x])
              start_time = timeit.default_timer()
              for c in range(count):
                  x(fpath)
              print('Result', timeit.default_timer() - start_time, '\n')
          

          它更快是因为 for 循环比 while 循环快(来源:herehere)。

          【讨论】:

            【解决方案7】:

            将以上2个代码合并如下:

            try:
                fd = open(decompressedFile,"rb")
            except IOError:
                logging.error("Unable to open the file in readmode:" + decompressedFile)
                return 4
            eachLine = fd.readline()
            prev = 0
            while eachLine:
                prev = zlib.crc32(eachLine, prev)
                eachLine = fd.readline()
            fd.close()
            

            【讨论】:

              【解决方案8】:

              您可以像 [ERD45FTR] 一样使用 base64 来退出。并且 zlib.crc32 提供了更新选项。

              import os, sys
              import zlib
              import base64
              
              

              def crc(fileName): fd = open(fileName,"rb") content = fd.readlines() fd.close() prev = None for eachLine in content: if not prev: prev = zlib.crc32(eachLine) else: prev = zlib.crc32(eachLine, prev) return prev

              for eachFile in sys.argv[1:]: print base64.b64encode(str(crc(eachFile)))

              【讨论】:

              • 感谢语法。我得到 LTc3NzI0ODI2,但我想要 E45A12AC(8 位数字)。试过base32,base16。
              【解决方案9】:

              使用 binascii 计算 CRC 有更快、更紧凑的方法:

              import binascii
              
              def crc32(filename):
                  buf = open(filename,'rb').read()
                  hash = binascii.crc32(buf) & 0xFFFFFFFF
                  return "%08X" % hash
              

              【讨论】:

                【解决方案10】:

                解决方案:

                import os, sys
                import zlib
                
                def crc(fileName, excludeLine="", includeLine=""):
                  try:
                        fd = open(fileName,"rb")
                  except IOError:
                        print "Unable to open the file in readmode:", filename
                        return
                  eachLine = fd.readline()
                  prev = None
                  while eachLine:
                      if excludeLine and eachLine.startswith(excludeLine):
                            continue   
                      if not prev:
                        prev = zlib.crc32(eachLine)
                      else:
                        prev = zlib.crc32(eachLine, prev)
                      eachLine = fd.readline()
                  fd.close()    
                  return format(prev & 0xFFFFFFFF, '08x') #returns 8 digits crc
                
                for eachFile in sys.argv[1:]:
                    print crc(eachFile)
                

                不知道什么是 (excludeLine="", includeLine="")...

                【讨论】:

                • 我知道这很古老,但我还是会解释一下。我给你投了反对票,因为我认为发布你不理解的代码没有用。