zlib压缩字节数组?

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

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 

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

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

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

.NET中的deflate逻辑表现出行为exception,其中先前压缩的数据实际上可以在“压缩”时显着膨胀。 这是与微软提出的Connect错误的根源,并在此处讨论过 。 就无效压缩而言,这可能就是您所看到的。 微软拒绝了这个错误,因为虽然它对节省空间无效,但压缩流并不是无效的,换句话说,它可以被任何兼容的DEFLATE引擎“解压缩”。

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


我想你想要一个ZLIB流。

DotNetZip项目中的免费托管Zlib实现了所有三种格式(DEFLATE,ZLIB,GZIP)的压缩流。 DeflateStream和GZipStream的工作方式与.NET内置类的工作方式相同,并且在那里有一个ZlibStream类,它可以完成您的想法。 这些类中没有一个表现出我上面描述的行为exception。


在代码中它看起来像这样:

  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); 

您可以在静态CompressBuffer方法上查看文档 。


编辑

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

如果你不想要“最大”压缩,换句话说,如果你已经设置为获得78 9C作为前两个字节,即使它没关系,那么你不能使用CompressBuffer便利function,它使用最好的封面下的压缩等级。 相反,你可以这样做:

  var compress = new Func( 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 . 

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

您可以使用HexEdit(操作 – >压缩 – >设置)快速测试所有不同的zlib选项。 见http://www.hexedit.com 。 只需将压缩的字节粘贴到HexEdit并解压缩,我花了10分钟检查数据。 还尝试使用GZip和ZLib标头压缩您的orignal字节作为仔细检查。 请注意,您可能必须设置这些设置以获得您期望的字节。