[C# 實作筆記] 將 Zip壓縮檔內的文字檔轉至記憶體內的List集合

    目標:

  1. 在 C#.NET 程式內處理 *.zip 壓縮檔 (內含文字檔)的解壓縮 (decompress)。
  2. 將解壓縮後的文字檔讀進記憶體內 (memory),並將每行 (per-line)的字串 (string)儲存至 List 集合 (collection),以俾方便處理。
    主要做法:

  1. 因官方未提供處理解壓縮 .zip 壓縮格式機制,所以需下載 3rd party 的類別庫 (class library)。這裡以 SharpZipLib ( formerly NZipLib) 類別庫來處理解壓縮。
  2. 利用 SharpZipLib 內的 ZipInputStream 開啟 .zip 壓縮檔。
    string fname = @"C:\Download\TxtData.zip";
    ZipInputStream zipStream = new ZipInputStream(File.Open(fname, FileMode.Open));
  3. 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);
                    }
  4. 由於 MemoryStream 並未提供有效的方法 (method)來解析原文字檔內的每一行字串,所以再將其包裹至 (wrap)已新增的 StreamReader 物件。
    StreamReader reader = new StreamReader(memStream, System.Text.Encoding.Default);
  5. 利用 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);
  6. 完成! 而後即可利用如 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();
        }

文章導覽

   

共有 3 則迴響

  1. 我自己寫的是類似這樣的函式:

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

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *