介绍
近期用c#开发一个游戏的存档编辑工具需要用 Zlib 标准的 Deflate 算法对数据进行解压。 在 StackOverflow 上逛了一圈,发现 c# 比较常用到的方式是微软提供的 System.IO.Compression, zlib.net, 以及 ICSharpCode 的SharpZipLib。我简单的测试和包装了一下,便在这里分享一下成果以及我个人的看法。
System.IO.Compression
通常来说,使用c#开发时能用微软官方提供的工具就尽量用,一个是bug会比较少,维护会比较稳定。此外,官方提供的方案往往在优化上也会高于第三方工具。
虽然在.NET Framework 4.5 开始 System.IO.Compression.DeflateStream 也使用Zlib进行Deflate格式的压缩与解压了,但经过测试其压缩和解压结果与其他Zlib库有所不同.
仔细观察就会发现,用 DeflateStream 压缩后的数据开头比Zlib压缩的数据少两个字节,结尾比Zlib少四个字节; 这种输出格式叫做 Raw Deflate 。
经过查证,C# 提供的 DeflateStream只能压缩成或者解压这种Raw Deflate, 而不能处理标准的 Zlib Deflate 格式 (不过据说可以自己生成); 但反过来,Zlib 可以处理或生成这种不包含头尾数据的Raw Deflate.
当然,你也可以选择手动添加 header 和 trailer. 具体怎么添加可以阅读文末链接的参考资料,由于不是特别重要,我就偷个懒了。
以下是我使用此方法简单包装的压缩与解压数据的代码:
// 使用System.IO.Compression进行Deflate压缩 public static byte[] MicrosoftCompress(byte[] data) { MemoryStream uncompressed = new MemoryStream(data); // 这里举例用的是内存中的数据;需要对文本进行压缩的话,使用 FileStream 即可 MemoryStream compressed = new MemoryStream(); DeflateStream deflateStream = new DeflateStream(compressed, CompressionMode.Compress); // 注意:这里第一个参数填写的是压缩后的数据应该被输出到的地方 uncompressed.CopyTo(deflateStream); // 用 CopyTo 将需要压缩的数据一次性输入;也可以使用Write进行部分输入 deflateStream.Close(); // 在Close中,会先后执行 Finish 和 Flush 操作。 byte[] result = compressed.ToArray(); return result; }
// 使用System.IO.Compression进行Deflate解压 public static byte[] MicrosoftDecompress(byte[] data) { MemoryStream compressed = new MemoryStream(data); MemoryStream decompressed = new MemoryStream(); DeflateStream deflateStream = new DeflateStream(compressed, CompressionMode.Decompress); // 注意: 这里第一个参数同样是填写压缩的数据,但是这次是作为输入的数据 deflateStream.CopyTo(decompressed); byte[] result = decompressed.ToArray(); return result; }
zlib.net
zlib.net是一个非常小体量的开源的第三方工具。经过本人有限的研究和了解,这个库其实更像是一个半成品,许多功能都不完善,不过优点在于非常轻巧,而且与c++端使用 boost::iostreams::zlib 效果相同。
以下是用 zlib.net 提供的 ZOutputStream 类来压缩数据的代码
public static byte[] ZLibDotnetCompress(byte[] data) { MemoryStream compressed = new MemoryStream(); ZOutputStream outputStream = new ZOutputStream(compressed, 2); outputStream.Write(data, 0, data.Length); // 这里采用的是用 Write 来写入需要压缩的数据;也可以采用和上面一样的方法 outputStream.Close(); byte[] result = compressed.ToArray(); return result; }
以下是用zlib.net 提供的 ZInputStream 类来解压数据的代码
public static byte[] ZLibDotnetDecompress(byte[] data, int size) { MemoryStream compressed = new MemoryStream(data); ZInputStream <strong style="color:transparent">来源gaodai#ma#com搞@代~码$网</strong>inputStream = new ZInputStream(compressed); byte[] result = new byte[size]; // 由于ZInputStream 继承的是BinaryReader而不是Stream, 只能提前准备好输出的 buffer 然后用 read 获取定长数据。 inputStream.read(result, 0, result.Length); // 注意这里的 read 首字母是小写 return result; }