Python 同一个 gzip 文件的 md5 散列值不一致
Python md5 hashes of same gzipped file are inconsistent
我正在尝试使用 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'
我认为应该简单地压缩文件,散列它并连续三次显示相同的输出散列。但是,我得到的输出是:
zipping 0
hashing 0
7bd80798bce074c65928e0cf9d66cae4
zipping 1
hashing 1
a3bd4e126e0a156c5d86df75baffc294
zipping 2
hashing 2
85812a39f388c388cb25a35c4fac87bf
如果我省略了 gzip 这一步,而只是连续三次对同一个 gzip 文件进行哈希处理,我确实得到了三次相同的输出:
hashing 0
ccfddd10c8fd1140db0b218124e7e9d3
hashing 1
ccfddd10c8fd1140db0b218124e7e9d3
hashing 2
ccfddd10c8fd1140db0b218124e7e9d3
谁能解释一下这是怎么回事?问题一定是 gzip 过程每次都不同。但据我所知,DEFLATE 算法是霍夫曼编码后跟 LZ77(运行-长度编码的一种形式)或 LZ77 后跟霍夫曼,因此给定相同的输入应该产生相同的输出。
哦,等等....显然 gzip 将时间戳信息添加到 gzip 文件的 header,因此散列会有所不同。
压缩完全相同的内容会产生不同的 gzip 输出有多种原因:
- 压缩级别。您可以通过压缩级别参数进行控制。
- header中的原始文件名。如果您使用 gzip.GzipFile api 而不是 gzip.open api.
,您可以控制这一点
- 修改时间也在header里面,也可以用gzip.GzipFileapi来控制。
所以这里有一段代码演示了从 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))
我正在尝试使用 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'
我认为应该简单地压缩文件,散列它并连续三次显示相同的输出散列。但是,我得到的输出是:
zipping 0
hashing 0
7bd80798bce074c65928e0cf9d66cae4
zipping 1
hashing 1
a3bd4e126e0a156c5d86df75baffc294
zipping 2
hashing 2
85812a39f388c388cb25a35c4fac87bf
如果我省略了 gzip 这一步,而只是连续三次对同一个 gzip 文件进行哈希处理,我确实得到了三次相同的输出:
hashing 0
ccfddd10c8fd1140db0b218124e7e9d3
hashing 1
ccfddd10c8fd1140db0b218124e7e9d3
hashing 2
ccfddd10c8fd1140db0b218124e7e9d3
谁能解释一下这是怎么回事?问题一定是 gzip 过程每次都不同。但据我所知,DEFLATE 算法是霍夫曼编码后跟 LZ77(运行-长度编码的一种形式)或 LZ77 后跟霍夫曼,因此给定相同的输入应该产生相同的输出。
哦,等等....显然 gzip 将时间戳信息添加到 gzip 文件的 header,因此散列会有所不同。
压缩完全相同的内容会产生不同的 gzip 输出有多种原因:
- 压缩级别。您可以通过压缩级别参数进行控制。
- header中的原始文件名。如果您使用 gzip.GzipFile api 而不是 gzip.open api. ,您可以控制这一点
- 修改时间也在header里面,也可以用gzip.GzipFileapi来控制。
所以这里有一段代码演示了从 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))