實際應(yīng)用中有時候會遇到需要處理 ZIP 壓縮解壓的情況,這時候我們有大概三種選擇:
- 調(diào)用 rar.exe, unzip.exe 等
- 使用某現(xiàn)成庫
- 完全手寫
第一種雖然能完成任務(wù),但是沒法知曉結(jié)果。曾經(jīng)有人對說,可以抓命令行輸出結(jié)果來判斷……這種依靠界面文字來進行精確判斷的行為個人認(rèn)為相當(dāng)不靠譜。第三種,既然我是個“造輪主義”者,當(dāng)然說好,但是現(xiàn)在我不了解 ZIP 格式,也不了解 ZIP 算法,所以這個日后再說。今天我們就來切切實實地用一次輪子。
ZIP 相關(guān)的庫中比較有名的可能就是 ZLib 和 InfoZip(unzip60)了。InfoZip 我了解的不多,其外層接口似乎也不大好,一堆回調(diào)——回調(diào)是個很煩人的東西,專門用來打亂代碼結(jié)構(gòu)。另外,這個庫也已經(jīng)有好多年沒更新了吧,太久的東西給人的感覺總是不太舒服。ZLib 最新版本是 1.2.5,今年 4 月 19 日出的。確切的說,ZLib 可能并不是一個針對 ZIP 文件的庫,它只是一個針對 gzip 以及 deflate 算法的庫。它提供了一個叫做 minizip (contrib\minizip) 例子來給出操作 ZIP 文件的方法。下文將從 ZLib 出發(fā),歸結(jié)出兩個傻瓜接口:
BOOL ZipCompress(LPCTSTR lpszSourceFiles, LPCTSTR lpszDestFile);
BOOL ZipExtract(LPCTSTR lpszSourceFile, LPCTSTR lpszDestFolder);
要引入的源文件
- ZLib 主目錄下的代碼,除 minigzip.c、example.c 外;
- contrib\minizip 下的代碼,除 minizip.c、miniunz.c 外。
相關(guān) API
雖然 minizip 更像是個例子,但是除去其主程序 minizip.c 和 miniunz.c 后,剩下的部分我們可以看作是 ZLib 的一個上層庫,它封裝了與 ZIP 文件格式相關(guān)的操作。而 minizip.c 和 miniunz.c 就是我們要改寫的——把它從命令行程序改為上述傻瓜接口。minizip.c 和 miniunz.c 中用到的 API 主要有:
壓縮相關(guān):
- zipOpen64
- zipClose
- zipOpenNewFileInZip
- zipCloseFileInZip
- zipWriteInFileInZip
解壓相關(guān):
- unzOpen64
- unzClose
- unzGetGlobalInfo64
- unzGoToNextFile
- unzGetCurrentFileInfo64
- unzOpenCurrentFile
- unzCloseCurrentFile
- unzReadCurrentFile
想必看到這些名字都能猜到怎么用了吧。好的接口果然能帶給人愉悅的。minizip 中的這些函數(shù)有的是帶“64”的有的是不帶的,有的還有“2”、“3”、“4”版本。這里一律用帶 64 的,不帶“2”、“3”、“4”的。
具體操作
下文涉及的所有操作,其相關(guān)代碼都可以在 http://zlibwrap.codeplex.com/ 上找到(Change Set 2450)。這里就不貼長篇代碼了。另外有個 DLL版本 和 Lib版本,供拿來主義者用。
首先是壓縮操作。使用 zipOpen64 來打開/創(chuàng)建一個 ZIP 文件,然后開始遍歷要被放到壓縮包中去的文件。針對每個文件,先調(diào)用一次 zipOpenNewFileInZip,然后開始讀原始文件數(shù)據(jù),使用 zipWriteInFileInZip 來寫入到 ZIP 文件中去。zipOpenNewFileInZip 的第三個參數(shù)是一個 zip_fileinfo 結(jié)構(gòu),該結(jié)構(gòu)數(shù)據(jù)可全部置零,其中 dosDate 可用于填入一個時間(LastModificationTime)。它的第二個參數(shù)是 ZIP 中的文件名,若要保持目錄結(jié)構(gòu),該參數(shù)中可以保留路徑,如 foo/bar.txt。
解壓操作稍微復(fù)雜一點點。打開一個 ZIP 文件后,需要先使用 unzGetGlobalInfo64 來取得該文件的一些信息,來了解這個壓縮包里一共包含了多少個文件,等等。目前我們用得著的就是這個文件數(shù)目。然后開始遍歷 ZIP 中的文件,初始時自動會定位在第一個文件,以后處理完一個后用 unzGoToNextFile 來跳到下一個文件。對于每個內(nèi)部文件,可用 unzGetCurrentFileInfo64 來查內(nèi)部文件名。這個文件名和剛才 zipOpenNewFileInZip 的第二個參數(shù)是一樣的形式,所以有可能包含路徑。也有可能會以路徑分隔符(/)結(jié)尾,表明這是個目錄項(其實壓縮操作的時候也可以針對目錄寫入這樣的內(nèi)部文件,上面沒有做)。所以接下來要根據(jù)情況創(chuàng)建(多級)目錄。unzGetCurrentFileInfo64 的第三個參數(shù)是 unz_file_info64 結(jié)構(gòu),其中也有一項包含了 dosDate 信息,可以還原文件時間。對于非目錄的內(nèi)部文件,用 unzOpenCurrentFile,打開,然后 unzReadCurrentFile 讀取文件內(nèi)容,寫入到真實文件中。unzReadCurrentFile 返回 0 表示文件讀取結(jié)束。
局限性
- 只能壓縮、解壓采用 deflate 算法的 ZIP 文件。(不過此類 ZIP 應(yīng)該占了絕大多數(shù))
- 由于 minizip 中相關(guān) API 的限制,以及 ZIP 文件格式的限制,被壓縮/解壓的相關(guān)文件名必須與系統(tǒng)的當(dāng)前代碼頁相符合。(雖然 ZIP 格式最近一次更新加入了使用 UTF8 編碼文件名的選項,但是不能保證所遇到的 ZIP 文件都是新格式的,minizip 中似乎也沒有針對此選項做什么動作。)
尾聲
這是一篇低俗的文章,沒有什么思想性。僅僅是一個小記。有不當(dāng)之處歡迎批評指正。
祝大家中秋節(jié)快樂!
posted on 2010-09-22 23:57
溪流 閱讀(46735)
評論(75) 編輯 收藏 引用 所屬分類:
C++