【问题标题】:Python gzip and Java GZIPOutputStream give different resultsPython gzip 和 Java GZIPOutputStream 给出不同的结果
【发布时间】:2021-02-01 23:37:02
【问题描述】:

我正在尝试在 Python 中获取 gzip 字符串的哈希值,并且需要它与 Java 的相同。但是 Python 的 gzip 实现似乎与 Java 的 GZIPOutputStream 不同。

Pythongzip:

import gzip
import hashlib

gzip_bytes = gzip.compress(bytes('test', 'utf-8'))
gzip_hex = gzip_bytes.hex().upper()
md5 = hashlib.md5(gzip_bytes).hexdigest().upper()

>>>gzip_hex
'1F8B0800678B186002FF2B492D2E01000C7E7FD804000000'
>>>md5
'C4C763E9A0143D36F52306CF4CCC84B8'

JavaGZIPOutputStream:

import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class HelloWorld{
    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }
    
    public static String md5(byte[] bytes) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] thedigest = md.digest(bytes);
            return bytesToHex(thedigest);
        }
        catch (NoSuchAlgorithmException e){
            new RuntimeException("MD5 Failed", e);
        }
        return new String();
    }

     public static void main(String []args){
         String string = "test";
         final byte[] bytes = string.getBytes();
         try {
             final ByteArrayOutputStream bos = new ByteArrayOutputStream();
             final GZIPOutputStream gout = new GZIPOutputStream(bos);
             gout.write(bytes);
             gout.close();
             final byte[] encoded = bos.toByteArray();
             System.out.println("gzip: " + bytesToHex(encoded));
             System.out.println("md5: " + md5(encoded));
         }
         catch(IOException e)  {
             new RuntimeException("Failed", e);
         }
     }
}

打印:

gzip: 1F8B08000000000000002B492D2E01000C7E7FD804000000
md5: 1ED3B12D0249E2565B01B146026C389D

因此,两个 gzip 字节输出看起来非常相似,但略有不同。

1F8B0800678B186002FF2B492D2E01000C7E7FD804000000

1F8B08000000000000002B492D2E01000C7E7FD804000000

Python gzip.compress() 方法接受 compresslevel 0-9 范围内的参数。尝试了所有这些,但没有一个给出预期的结果。 有什么方法可以在 Python 中获得与 Java 的 GZIPOutputStream 相同的结果?

【问题讨论】:

    标签: java python gzip gzipoutputstream


    【解决方案1】:

    您的要求“在 Python 中压缩字符串的哈希并需要它与 Java 的相同”通常无法满足。你需要改变你的需求,以不同的方式实现你的需求。我建议只要求 解压缩 数据具有相同的哈希值。事实上,两个 gzip 字符串中已经存在解压缩数据的 32 位散列(CRC-32),它们是相同的 (0xd87f7e0c)。如果你想要一个更长的哈希,那么你可以附加一个。最后四个字节是未压缩的长度,模 232,因此您也可以比较它们。只需比较两个字符串的最后八个字节,并检查它们是否相同。

    您问题中两个 gzip 字符串之间的差异说明了这个问题。一个在标头中有时间戳,另一个没有(设置为零)。即使他们都有时间戳,他们仍然很可能是不同的。它们在标头中还有一些其他字节不同,例如原始操作系统。

    此外,您的示例中的压缩数据非常短,因此在这种情况下恰好是相同的。然而,对于任何合理数量的数据,两个 gzipper 生成的压缩数据会有所不同,除非它们恰好使用完全相同的 deflate 代码、相同版本的代码和相同的内存大小生成和压缩级别设置。如果您无法控制所有这些,那么在给定相同的未压缩数据的情况下,您将永远无法确保从它们中输出相同的压缩数据。

    简而言之,不要浪费时间尝试获取相同的压缩字符串。

    【讨论】:

    • 我试图简单地将“标题时间戳”替换为 00,例如:data = data[:8] + '000000000000' + data[20:] 并在不同的大量数据上对其进行了测试。看起来有帮助。结果现在是一样的。
    • 你没有在听。
    • 该 java 代码在 Android arm 和 windows x64 上返回了相同的结果。我不认为那里有一些“操作系统”字节。即使有一些 - 我也更愿意删除它们。这里的重点是模拟原始的 android 行为。
    • 还没听。
    猜你喜欢
    • 1970-01-01
    • 2012-12-14
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 2021-06-06
    • 2013-12-25
    • 1970-01-01
    相关资源
    最近更新 更多