【问题标题】:zlib compressing byte array?zlib压缩字节数组?
【发布时间】:2011-06-08 17:11:51
【问题描述】:

我有这个未压缩的字节数组:

0E 7C BD 03 6E 65 67 6C 65 63 74 00 00 00 00 00 00 00 00 00 42 52 00 00 01 02 01
00 BB 14 8D 37 0A 00 00 01 00 00 00 00 05 E9 05 E9 00 00 00 00 00 00 00 00 00 00
00 00 00 00 01 00 00 00 00 00 81 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 05 00 00 01 00 00 00

我需要使用 deflate 算法(在 zlib 中实现)对其进行压缩,从我在 C# 中搜索到的等效算法将使用 GZipStream,但我根本无法匹配压缩结果。

这是压缩代码:

public byte[] compress(byte[] input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        using (GZipStream deflateStream = new GZipStream(ms, CompressionMode.Compress))
        {
            deflateStream.Write(input, 0, input.Length);
        }
        return ms.ToArray();
    }
}

这是上面压缩代码的结果:

1F 8B 08 00 00 00 00 00 04 00 ED BD 07 60 1C 49 96 25 26 2F 6D CA 7B 7F 4A F5 4A
D7 E0 74 A1 08 80 60 13 24 D8 90 40 10 EC C1 88 CD E6 92 EC 1D 69 47 23 29 AB 2A
81 CA 65 56 65 5D 66 16 40 CC ED 9D BC F7 DE 7B EF BD F7 DE 7B EF BD F7 BA 3B 9D
4E 27 F7 DF FF 3F 5C 66 64 01 6C F6 CE 4A DA C9 9E 21 80 AA C8 1F 3F 7E 7C 1F 3F
22 7E 93 9F F9 FB 7F ED 65 7E 51 E6 D3 F6 D7 30 CF 93 57 BF C6 AF F1 6B FE 5A BF
E6 AF F1 F7 FE 56 7F FC 03 F3 D9 AF FB 5F DB AF 83 E7 0F FE 35 23 1F FE BA F4 FE
AF F1 6B FC 1A FF 0F 26 EC 38 82 5C 00 00 00

这是我期待的结果:

78 9C E3 AB D9 CB 9C 97 9A 9E 93 9A 5C C2 00 03 4E 41 0C 0C 8C 4C 8C 0C BB 45 7A
CD B9 80 4C 90 18 EB 4B D6 97 0C 28 00 2C CC D0 C8 C8 80 09 58 21 B2 00 65 6B 08
C8

我做错了什么,有人可以帮我吗?

【问题讨论】:

  • 为什么您期望不同实现的输出相同?有很多方法可以压缩一些可以用同一个解压器解压的内容。但在你的情况下,zip流似乎输出了某种标题。
  • GZipStream 的结果不仅不同,而且比未压缩的输入要大!
  • @Inuyasha 我已经明白了,这就是为什么我正在寻找如何通过试图找出我做错了什么来使它们相等,正如我所提到的,我需要使用 zlib 的 deflate 实现在 C# 中。 @CodeInChaos 我不知道这是我在 SO 周围搜索的不同实现,我发现一些回复说 GZip 是它的等价物,我确实发现它不是当我开始测试它时。
  • 除了增加的大小之外,我认为还有另一个程序解压缩这个。怎么样?

标签: c# bytearray zlib compression


【解决方案1】:

首先,一些信息:DEFLATE是压缩算法,在RFC 1951中定义。 DEFLATE 用于 ZLIB 和 GZIP 格式,分别在 RFC 19501952 中定义,它们本质上是 DEFLATE 字节流的瘦包装器。包装器提供元数据,例如文件名、时间戳、CRC 或 Adlers 等。

.NET 的基类库实现了一个 DeflateStream,它在用于压缩时会产生一个原始的 DEFLATE 字节流。当用于解压时,它会消耗一个原始的 DEFLATE 字节流。 .NET 还提供了 GZipStream,它只是围绕该基础的 GZIP 包装器。 .NET 基类库中没有 ZlibStream - 没有任何东西产生或使用 ZLIB。有一些技巧,你可以搜索一下。

.NET 中的 deflate 逻辑表现出异常行为,以前压缩的数据在“压缩”时实际上可能会显着膨胀。这是a Connect bug raised with Microsofthas been discussed here on SO 的来源。就无效压缩而言,这可能就是您所看到的。微软拒绝了这个错误,因为虽然它对节省空间无效,但压缩流并不是无效的,换句话说,它可以被任何兼容的 DEFLATE 引擎“解压缩”。

无论如何,正如其他人发布的那样,不同压缩器产生的压缩字节流可能不一定相同。这取决于它们的默认设置,以及压缩器的应用程序指定设置。即使压缩的字节流不同,它们仍可能解压缩为相同的原始字节流。另一方面,您用来压缩的东西是 GZIP,而您想要似乎是 ZLIB。虽然它们是相关的,但它们并不相同;您不能使用 GZipStream 生成 ZLIB 字节流。这是您看到的差异的主要来源。


我想你想要一个 ZLIB 流。

DotNetZip project 中的免费托管 Zlib 实现了所有三种格式(DEFLATE、ZLIB、GZIP)的压缩流。 DeflateStream 和 GZipStream 的工作方式与 .NET 内置类相同,并且其中有一个 ZlibStream 类,可以按照您的想法进行操作。这些类都没有表现出我上面描述的行为异常。


在代码中是这样的:

    byte[] original = new byte[] {
        0x0E, 0x7C, 0xBD, 0x03, 0x6E, 0x65, 0x67, 0x6C,
        0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x42, 0x52, 0x00, 0x00,
        0x01, 0x02, 0x01, 0x00, 0xBB, 0x14, 0x8D, 0x37,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x05, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00
    };

    var compressed = Ionic.Zlib.ZlibStream.CompressBuffer(original);

输出是这样的:

0000    78 DA E3 AB D9 CB 9C 97 9A 9E 93 9A 5C C2 00 03     x...........\...
0010    4E 41 0C 0C 8C 4C 8C 0C BB 45 7A CD 61 62 AC 2F     NA...L...Ez.ab./
0020    19 B0 82 46 46 2C 82 AC 40 FD 40 0A 00 35 25 07     ...FF,..@.@..5%.
0030    CE                                                  .

要解压,

    var uncompressed = Ionic.Zlib.ZlibStream.UncompressBuffer(compressed);

你可以看到the documentation on the static CompressBuffer method


编辑

问题出现了,为什么 DotNetZip 为前两个字节生成 78 DA 而不是 78 9C?差异是无关紧要的。 78 DA 编码“最大压缩”,而78 9C 编码“默认压缩”。正如您在数据中看到的那样,对于这个小样本,无论使用 BEST 还是 DEFAULT,实际压缩字节都完全相同。此外,在解压缩期间不使用压缩级别信息。它对您的应用程序没有影响。

如果您不想要“最大”压缩,换句话说,如果您非常愿意将78 9C 作为前两个字节,即使这无关紧要,那么您也不能使用CompressBuffer 便利函数,它在幕后使用最佳压缩级别。相反,您可以这样做:

  var compress = new Func<byte[], byte[]>( a => {
        using (var ms = new System.IO.MemoryStream())
        {
            using (var compressor =
                   new Ionic.Zlib.ZlibStream( ms, 
                                              CompressionMode.Compress,
                                              CompressionLevel.Default )) 
            {
                compressor.Write(a,0,a.Length);
            }

            return ms.ToArray();
        }
    });

  var original = new byte[] { .... };
  var compressed = compress(original);

结果是:

0000    78 9C E3 AB D9 CB 9C 97 9A 9E 93 9A 5C C2 00 03     x...........\...
0010    4E 41 0C 0C 8C 4C 8C 0C BB 45 7A CD 61 62 AC 2F     NA...L...Ez.ab./
0020    19 B0 82 46 46 2C 82 AC 40 FD 40 0A 00 35 25 07     ...FF,..@.@..5%.
0030    CE                                                  .

【讨论】:

  • @Cheeso 我刚刚从 Merlyn 的回复中尝试了 ZLib.Net,它可以很好地压缩给我我期望的数据,现在我只是不知道如何解压缩我拥有的字节数组收到。
  • @Cheeso 谢谢使用它解压缩似乎相当简单我会尝试一下,因为我在从其他库解压缩它时遇到了一些问题。
  • @Cheeso DotNetZip 总是在一开始就用 1 个不同的字节“78 DA”而不是“78 9C”压缩它,而当我使用 ZLib.Net 时它工作正常,给我 9C 而不是 DA,删除它可以很好地解压缩不确定为什么它将 9C 更改为 DA...
  • 第二个字节是 9C 还是 DA 并不重要。 ZLIB 有一个 2 字节的头,如果使用 DEFLATE,第一个字节表示压缩方法和窗口大小。它总是 78。下一个字节变化,并指示 3 件事:是否使用了预设字典、压缩级别和第一个两个字节的各种校验和。实际上,9C 表示压缩级别“默认”,而 DA 表示压缩级别“最大”。解压不需要此信息;只有当您的应用程序考虑额外的压缩是否有用时,它才有意义。你可以忽略它。
  • CompressBuffer 便捷方法指定“最佳压缩”,这就是为什么它在您看到的输出缓冲区中编码为78 DA
【解决方案2】:

很简单,你得到了一个 GZip 标头。你想要的是更简单的 Zlib 头文件。 ZLib 具有 GZip 标头、Zlib 标头或无标头选项。通常使用 Zlib 标头,除非数据与磁盘文件相关联(在这种情况下使用 GZip 标头。)显然,.Net 库无法编写 zlib 标头(尽管这是迄今为止最常见的文件格式中使用的标头)。试试http://dotnetzip.codeplex.com/

您可以使用 HexEdit(操作->压缩->设置)快速测试所有不同的 zlib 选项。见http://www.hexedit.com。我花了 10 分钟来检查您的数据,只需将压缩字节粘贴到 HexEdit 并解压缩。还尝试使用 GZip 和 ZLib 标头压缩原始字节作为双重检查。请注意,您可能需要调整设置才能准确获取您期望的字节数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多