使用带有一个或两个内存流的gZipStream会产生很大的不同

Mar*_*rek 2 c# compression memorystream gzipstream binaryformatter

我肯定错过了一些非常明显的东西,但任何人都可以解释为什么在第二种情况下有更好的压缩率?!

案例1:压缩率非常低,有时甚至会增加尺寸.

using (var memoryStream = new System.IO.MemoryStream())
using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress))
{
  new BinaryFormatter().Serialize(gZipStream, obj);
  gZipStream.Close();
  return memoryStream.ToArray();
}
Run Code Online (Sandbox Code Playgroud)

案例2:更好的压缩,我没有得到规模增长.

using (MemoryStream msCompressed = new MemoryStream())
using (GZipStream gZipStream = new GZipStream(msCompressed, CompressionMode.Compress))
using (MemoryStream msDecompressed = new MemoryStream())
{
  new BinaryFormatter().Serialize(msDecompressed, obj);
  byte[] byteArray = msDecompressed.ToArray();

  gZipStream.Write(byteArray, 0, byteArray.Length);
  gZipStream.Close();
  return msCompressed.ToArray();
}
Run Code Online (Sandbox Code Playgroud)

我已经完成了镜像解压缩,在这两种情况下我都可以将它反序列化为源对象而不会出现任何问题.

以下是一些统计数据:

UncSize:58062085B,Comp1:46828139B,0.81%

UncSize:58062085B,Comp2:31326029B,0.54%

UncSize:7624735B,Comp1:7743947B,1.02%

UncSize:7624735B,Comp2:5337522B,0.70%

UncSize:1237628B,Comp1:1265406B,1.02%

UncSize:1237628B,Comp2:921695B,0.74%

Jim*_*hel 7

您没有说明您正在使用的是哪个版本的.NET.在4.0之前的版本中,GZipStream基于每个写入压缩数据.也就是说,它会压缩您发送给它的缓冲区.在第一个示例中,该Serialize方法可能会向流中写入非常小的缓冲区(一次一个字段).在第二个示例中,Serialize将整个对象序列化为内存流,然后将内存流的缓冲区写入GZipStream一个大块中.GZipStream当它有一个更大的缓冲区(64K接近最佳)时可以做得更好.

在.NET 4.0中可能仍然如此.我不记得我是否测试过它.

我过去处理这个问题的方法是使用BufferedStream:

using (var mstream = new MemoryStream())
{
    using (var bstream = new BufferedStream(new GZipStream(mstream, CompressionMode.Compress), 65536))
    {
        new BinaryFormatter().Serialize(btream, obj);
    }
    return mstream.ToArray();
}
Run Code Online (Sandbox Code Playgroud)

这样,压缩器就可以使用64K缓冲区了.

在.NET 4.0之前,提供大于64K的缓冲区没有任何好处GZipStream.我已经看到一些信息表明.NET 4.0中的压缩器可以通过更大的缓冲区更好地进行压缩.但是,我自己没有测试过.