- 目標:
- 在 C#.NET 程式內處理 *.zip 壓縮檔 (內含文字檔)的解壓縮 (decompress)。
- 將解壓縮後的文字檔讀進記憶體內 (memory),並將每行 (per-line)的字串 (string)儲存至 List 集合 (collection),以俾方便處理。
- 主要做法:
- 因官方未提供處理解壓縮 .zip 壓縮格式機制,所以需下載 3rd party 的類別庫 (class library)。這裡以 SharpZipLib ( formerly NZipLib) 類別庫來處理解壓縮。
- 利用 SharpZipLib 內的 ZipInputStream 開啟 .zip 壓縮檔。
string fname = @"C:\Download\TxtData.zip"; ZipInputStream zipStream = new ZipInputStream(File.Open(fname, FileMode.Open));
- ZipInputStream 會依據所指定的緩衝區大小,依序將已解壓縮的文字檔內容讀至「位元組陣列 (Byte array)」;同時再利用已新增的 MemoryStream (記憶體串流)物件讀進 Byte array。
MemoryStream memStream = new MemoryStream(); int bufferSize = 2048; //指定的緩衝區大小 int readCount = 0; //回傳已讀取的位元組數目 byte[] buffer = new byte[bufferSize]; readCount = zipStream.Read(buffer, 0, bufferSize); while (readCount > 0) { memStream.Write(buffer, 0, readCount); readCount = zipStream.Read(buffer, 0, bufferSize); }
- 由於 MemoryStream 並未提供有效的方法 (method)來解析原文字檔內的每一行字串,所以再將其包裹至 (wrap)已新增的 StreamReader 物件。
StreamReader reader = new StreamReader(memStream, System.Text.Encoding.Default);
- 利用 StreamReader 的 ReadLine() 方法,讀進每一行的字串 (string),並循序加入 List 集合 (collection)內。
memStream.Seek(0, SeekOrigin.Begin); // reset file pointer List<string> list = new List<string>(); string str; while ((str = reader.ReadLine()) != null) list.Add(str);
- 完成! 而後即可利用如 LINQ (Language-Integrated Query)存取並篩選 List 集合內的字串資料。
完整的程式碼片段 (code-snippet):
private void decompress() { string fname = @"C:\Download\TxtData.zip"; ZipInputStream zipStream = new ZipInputStream(File.Open(fname, FileMode.Open)); ZipEntry entry = zipStream.GetNextEntry(); while (entry != null) { if (!entry.IsFile) { continue; } MemoryStream memStream = new MemoryStream(); int bufferSize = 2048; //指定的緩衝區大小 int readCount = 0; //回傳已讀取的位元組數目 byte[] buffer = new byte[bufferSize]; readCount = zipStream.Read(buffer, 0, bufferSize); while (readCount > 0) { memStream.Write(buffer, 0, readCount); readCount = zipStream.Read(buffer, 0, bufferSize); } //需指定預設的編碼,否則讀進資料可能會亂碼 StreamReader reader = new StreamReader(memStream, System.Text.Encoding.Default); memStream.Seek(0, SeekOrigin.Begin); // reset file pointer List<string> list = new List<string>(); string str; while ((str = reader.ReadLine()) != null) list.Add(str); //release resource reader.Close(); reader.Dispose(); memStream.Close(); memStream.Dispose(); entry = zipStream.GetNextEntry(); } zipStream.Close(); zipStream.Dispose(); } |
我自己寫的是類似這樣的函式:
static IEnumerable ReadLines(string zipfile, Encoding defaultencoding)
{
using (var fs = new FileStream(zipfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var zipfs = new ZipInputStream(fs))
{
zipfs.IsStreamOwner = false;
ZipEntry entry;
while ((entry = zipfs.GetNextEntry()) != null)
{
if (!entry.IsFile)
continue;
var sr = new StreamReader(zipfs, defaultencoding, true);
string line;
while ((line = sr.ReadLine()) != null)
yield return line;
}
}
}
抱歉, 上面那個回傳的形態應該是
IEnumerable<String>
您寫的程式碼簡潔太多了。
感謝分享,學到一招,對我幫助很大。 ^^