如何在c#中使用Zlib壓縮與解壓
介紹
近期用c#開發(fā)一個游戲的存檔編輯工具需要用 Zlib 標準的 Deflate 算法對數(shù)據(jù)進行解壓。 在 StackOverflow 上逛了一圈,發(fā)現(xiàn) c# 比較常用到的方式是微軟提供的 System.IO.Compression, zlib.net, 以及 ICSharpCode 的SharpZipLib。我簡單的測試和包裝了一下,便在這里分享一下成果以及我個人的看法。
System.IO.Compression
通常來說,使用c#開發(fā)時能用微軟官方提供的工具就盡量用,一個是bug會比較少,維護會比較穩(wěn)定。此外,官方提供的方案往往在優(yōu)化上也會高于第三方工具。
雖然在.NET Framework 4.5 開始 System.IO.Compression.DeflateStream 也使用Zlib進行Deflate格式的壓縮與解壓了,但經(jīng)過測試其壓縮和解壓結果與其他Zlib庫有所不同.
仔細觀察就會發(fā)現(xiàn),用 DeflateStream 壓縮后的數(shù)據(jù)開頭比Zlib壓縮的數(shù)據(jù)少兩個字節(jié),結尾比Zlib少四個字節(jié); 這種輸出格式叫做 Raw Deflate 。
經(jīng)過查證,C# 提供的 DeflateStream只能壓縮成或者解壓這種Raw Deflate, 而不能處理標準的 Zlib Deflate 格式 (不過據(jù)說可以自己生成); 但反過來,Zlib 可以處理或生成這種不包含頭尾數(shù)據(jù)的Raw Deflate.
當然,你也可以選擇手動添加 header 和 trailer. 具體怎么添加可以閱讀文末鏈接的參考資料,由于不是特別重要,我就偷個懶了。
以下是我使用此方法簡單包裝的壓縮與解壓數(shù)據(jù)的代碼:
// 使用System.IO.Compression進行Deflate壓縮 public static byte[] MicrosoftCompress(byte[] data) { MemoryStream uncompressed = new MemoryStream(data); // 這里舉例用的是內存中的數(shù)據(jù);需要對文本進行壓縮的話,使用 FileStream 即可 MemoryStream compressed = new MemoryStream(); DeflateStream deflateStream = new DeflateStream(compressed, CompressionMode.Compress); // 注意:這里第一個參數(shù)填寫的是壓縮后的數(shù)據(jù)應該被輸出到的地方 uncompressed.CopyTo(deflateStream); // 用 CopyTo 將需要壓縮的數(shù)據(jù)一次性輸入;也可以使用Write進行部分輸入 deflateStream.Close(); // 在Close中,會先后執(zhí)行 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); // 注意: 這里第一個參數(shù)同樣是填寫壓縮的數(shù)據(jù),但是這次是作為輸入的數(shù)據(jù) deflateStream.CopyTo(decompressed); byte[] result = decompressed.ToArray(); return result; }
zlib.net
zlib.net是一個非常小體量的開源的第三方工具。經(jīng)過本人有限的研究和了解,這個庫其實更像是一個半成品,許多功能都不完善,不過優(yōu)點在于非常輕巧,而且與c++端使用 boost::iostreams::zlib 效果相同。
以下是用 zlib.net 提供的 ZOutputStream 類來壓縮數(shù)據(jù)的代碼
public static byte[] ZLibDotnetCompress(byte[] data) { MemoryStream compressed = new MemoryStream(); ZOutputStream outputStream = new ZOutputStream(compressed, 2); outputStream.Write(data, 0, data.Length); // 這里采用的是用 Write 來寫入需要壓縮的數(shù)據(jù);也可以采用和上面一樣的方法 outputStream.Close(); byte[] result = compressed.ToArray(); return result; }
以下是用zlib.net 提供的 ZInputStream 類來解壓數(shù)據(jù)的代碼
public static byte[] ZLibDotnetDecompress(byte[] data, int size) { MemoryStream compressed = new MemoryStream(data); ZInputStream inputStream = new ZInputStream(compressed); byte[] result = new byte[size]; // 由于ZInputStream 繼承的是BinaryReader而不是Stream, 只能提前準備好輸出的 buffer 然后用 read 獲取定長數(shù)據(jù)。 inputStream.read(result, 0, result.Length); // 注意這里的 read 首字母是小寫 return result; }
你需要通過read來獲取解壓后的數(shù)據(jù),同時你要在調用其解壓的方法時提前提供好外部的buffer用于儲存輸出的數(shù)據(jù),這個buffer的大小就是一個問題了。
如果打算使用這個的話,建議除了儲存壓縮的數(shù)據(jù)以外,在不會被壓縮的位置添加壓縮前大小的數(shù)據(jù)。
但總體來說,個人不建議使用這個工具。
https://github.com/zyborg/zlib.net
http://www.componentace.com/zlib_.NET.htm
SharpZipLib
我最終選擇使用的是 SharpZipLib. (編輯:當時沒做速度測試,且我需要解壓的文件不是太大,速度也不是很重要,否則的話不推薦選擇這個方案。。。)
ICSharpCode 不愧是開發(fā)了 ILSpy 的團隊,SharpZipLib 在提供強大的功能的同時,使用也很方便。限于主題,這里只討論用 Deflate 格式來壓縮數(shù)據(jù)流。
簡單來說,你需要做的就是通過 DeflaterOutputStream 來壓縮,InflaterInputStream 來解壓,而除了壓縮和解壓分在兩個不同的類以外,其他的操作方式和 System.IO.Compression.DeflateStream 可以做到完全一樣。
而且其壓縮和解壓的結果和直接使用Zlib官方的庫一模一樣,開發(fā)輔助其他程序的工具時不用擔心頭尾數(shù)據(jù)的問題,算是非常省事了。
以下是我使用該方案簡單包裝的方法:
public static byte[] SharpZipLibCompress(byte[] data) { MemoryStream compressed = new MemoryStream(); DeflaterOutputStream outputStream = new DeflaterOutputStream(compressed); outputStream.Write(data, 0, data.Length); outputStream.Close(); return compressed.ToArray(); }
public static byte[] SharpZipLibDecompress(byte[] data) { MemoryStream compressed = new MemoryStream(data); MemoryStream decompressed = new MemoryStream(); InflaterInputStream inputStream = new InflaterInputStream(compressed); inputStream.CopyTo(decompressed); return decompressed.ToArray(); }
速度對比
為了對比幾種方法在壓縮與解壓效率上的優(yōu)劣,我準備了兩組數(shù)據(jù)做了一個簡單的測試。
第一組為短數(shù)據(jù),是一個簡單的字符串 "this is just a string for testing, see how this compression thing works."
第二組為長數(shù)據(jù),是在網(wǎng)上下載到的英文版的 《冰與火之歌:權利的游戲》txt文本,大小約1.7mb。
我分別用每個方法壓縮和解壓短數(shù)據(jù)1000次,長數(shù)據(jù)100次, 最終的結果如下:
Length of Short Data: 144 Length of Long Data: 1685502 ============================================ Compress and decompress with Microsoft Zlib Compression (1000 times): 54 Compress and decompress with Microsoft Zlib Compression (long data 100 times): 7924 ============================================ Compress and decompress with Zlib.net Compression (1000 times): 254 Compress and decompress with Zlib.net Compression (long data 100 times): 9924 ============================================ Compress and decompress with SharpZipLib Compression (1000 times): 442 Compress and decompress with SharpZipLib Compression (long data 100 times): 26782
顯而易見的,無論是長數(shù)據(jù)還是短數(shù)據(jù)的壓縮與解壓,System.IO.Compression中提供的方法都優(yōu)于另外兩種方法。
Zlib.net在速度上的劣勢不明顯,而同樣的算法SharpZipLib要花兩到三倍的時間。
總結
最終,不出所料的,微軟官方提供的 System.IO.Compression 中的方法在速度上有著明顯的優(yōu)勢;雖然不會提供Deflate的頭尾信息,但可以想辦法自己生成,而且這一缺點基本上是可以完全忽略的。 Zlib.net 雖然在速度上表現(xiàn)也不錯,同時也會生成Deflate壓縮的頭尾信息,但因為其包裝比較潦草,使用起來相對不方便。而 SharpZipLib 很可惜,雖然其他各方面都很方便,但速度上的缺陷相當致命,只能在一定需要 Deflate 而非 RawDeflate 或者使用的.Net Framework早于4.5的時候(且運行中時間消耗不重要)偷懶的用一用了。
參考與延申
關于Zlib
關于 Deflate 和 Raw Deflate
https://stackoverflow.com/questions/37845440/net-deflatestream-vs-linux-zlib-difference
https://www.ietf.org/rfc/rfc1950.txt
https://www.ietf.org/rfc/rfc1951.txt
關于CSharp System.IO.Compression.DeflateStream
https://docs.microsoft.com/en-us/dotnet/api/system.io.compression.deflatestream?view=net-5.0
開發(fā)者之一 Mark Adler 在 StackOverflow 上的回答
deflate 和 compress 函數(shù)的區(qū)別
如何手動添加 header 和 trailer
https://stackoverflow.com/questions/39939869/data-format-for-system-io-compression-deflatestream
以上就是如何再c#中使用Zlib壓縮與解壓的詳細內容,更多關于c#使用Zlib壓縮與解壓的資料請關注腳本之家其它相關文章!
相關文章
C#使用Newtonsoft.Json中的JObject對象
本文詳細講解了C#使用Newtonsoft.Json中JObject對象的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-07-07C# datagridview、datagrid、GridControl增加行號代碼解析
今天這篇文章小編就來給大家分享關于C# datagridview、datagrid、GridControl增加行號的介紹,主要包括WinForm中datagridview增加行號、WPF中datagrid增加行號、WPF dev控件GridControl增加行號三個內容,感興趣等我小伙伴可以參考一下2021-10-10C#?使用原生?System.IO.Compression?實現(xiàn)?zip?的壓縮與解壓
這篇文章主要介紹了C#?使用原生?System.IO.Compression?實現(xiàn)?zip?的壓縮與解壓,zip?是一個非常常見的壓縮包格式,本文主要用于說明如何使用代碼?文件或文件夾壓縮為?zip壓縮包及其解壓操作,需要的朋友可以參考下2022-09-09淺析C#靜態(tài)類,靜態(tài)構造函數(shù),靜態(tài)變量
這篇文章主要介紹了淺析C#靜態(tài)類,靜態(tài)構造函數(shù),靜態(tài)變量 的相關資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-06-06