相同gzip压缩文件的Python md5哈希值不一致

sha*_*oor 5 python hash gzip md5

我正在尝试使用python模块gzip压缩文件,然后使用hashlib对gzip归档的文件进行哈希处理。我有以下代码:

import hashlib
import gzip

f_name = 'read_x.fastq'

for x in range(0,3):

    file = open(f_name, 'rb')

    myzip = gzip.open('test.gz', 'wb', compresslevel=1)

    n = 100000000
    try:
        print 'zipping ' + str(x)
        for chunk in iter(lambda: file.read(n), ''):
            myzip.write(chunk)
    finally:
        file.close()
        myzip.close()

    md5 = hashlib.md5()
    print 'hashing ' + str(x)
    with open('test.gz', 'r') as f:
        for chunk in iter(lambda: f.read(n), ''):
            md5.update(chunk)

    print md5.hexdigest()
    print '\n'
Run Code Online (Sandbox Code Playgroud)

我认为应该简单地压缩文件,对其进行哈希处理并连续显示三次相同的输出哈希。但是,我得到的输出是:

zipping 0
hashing 0
7bd80798bce074c65928e0cf9d66cae4


zipping 1
hashing 1
a3bd4e126e0a156c5d86df75baffc294


zipping 2
hashing 2
85812a39f388c388cb25a35c4fac87bf
Run Code Online (Sandbox Code Playgroud)

如果我省略了gzip步骤,并且只是连续对同一个gzip压缩文件进行了三次哈希处理,那么我确实确实获得了三次相同的输出:

hashing 0
ccfddd10c8fd1140db0b218124e7e9d3


hashing 1
ccfddd10c8fd1140db0b218124e7e9d3


hashing 2
ccfddd10c8fd1140db0b218124e7e9d3
Run Code Online (Sandbox Code Playgroud)

谁能解释这是怎么回事?问题必须是gzip进程每次都不同。但是据我所知,DEFLATE算法是霍夫曼编码,然后是LZ77(游程长度编码的一种形式),或者是LZ77,然后是霍夫曼,因此给定相同的输入应产生相同的输出。

Mar*_*zer 5

压缩完全相同的内容会产生不同的gzip输出有几个原因:

  • 压缩级别。您可以通过compress level参数来控制。
  • 标头中的原始文件的名称。您可以控制是否使用gzip.GzipFile api而不是gzip.open api。
  • 修改时间也位于标头中,也可以使用gzip.GzipFile api进行控制。

因此,以下这段代码演示了从python gzip获取可再现输出的错误和正确方法:

import hashlib
import gzip

f_name = '/etc/passwd'
output_template = '/tmp/test{}.gz'

def digest(filename: str) -> str:
    md5 = hashlib.md5()
    with open(output_filename, 'rb') as f:
        for chunk in iter(lambda: f.read(block_size), b''):
            md5.update(chunk)
    return md5.hexdigest()

print("The default way - non identical outputs")
for x in range(0,3):
    input_handle = open(f_name, 'rb')
    output_filename = output_template.format(x)
    myzip = gzip.open(output_filename, 'wb')
    block_size = 4096
    try:
        for chunk in iter(lambda: input_handle.read(block_size), b''):
            myzip.write(chunk)
    finally:
        input_handle.close()
        myzip.close()
    print(digest(output_filename))

print("The right way to get identical outputs")
for x in range(3,6):
    input_handle = open(f_name, 'rb')
    output_filename = output_template.format(x)
    myzip = gzip.GzipFile(
        filename='',  # do not emit filename into the output gzip file
        mode='wb',
        fileobj=open(output_filename, 'wb'),
        mtime=0,
    )
    block_size = 4096
    try:
        for chunk in iter(lambda: input_handle.read(block_size), b''):
            myzip.write(chunk)
    finally:
        input_handle.close()
        myzip.close()
    print(digest(output_filename))
Run Code Online (Sandbox Code Playgroud)