Linux下使用zlib實作檔案壓縮解壓
- 1.背景
- 2.思路
- 3.實作
-
- 3.1 函數入口
- 3.2 壓縮檔案
- 3.3 解壓檔案
- 4.運作
- 5.結論
1.背景
上篇文章《Linux下使用zlib實作資料壓縮解壓》對文本資料進行壓縮處理,本文再進行zlib的案例進行學習,這次整理出了檔案壓縮例子。
2.思路
檢視 zlib-1.2.11/examples/zpipe.c代碼,發現他的處理是從stdin擷取原始資料,再從stdout輸出處理後的資料。那麼我們可以将他改成從讀取指定檔案内容,壓縮、解壓處理輸出到指定檔案中去。
3.實作
3.1 函數入口
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <assert.h>
#define SIZE_CHUNK (8 * 1024)
int main(int argc, char *argv[])
{
int res = -1;
if (argv[1] && argv[2] && argv[3] && 0 == strcmp(argv[1], "-d")) {
/* -d模式 表示壓縮檔案 */
res = file_deflate(argv[2], argv[3]);
}
else if (argv[1] && argv[2]) {
/* 解壓檔案 */
res = file_inflate(argv[1], argv[2]);
}
else {
printf("%s [ -d ] < source > < dest >\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Result:\t\t\t\t[%s]\n", res ? "Failure" : "Success");
exit(res ? EXIT_FAILURE : EXIT_SUCCESS);
}
3.2 壓縮檔案
壓縮過程主要是大循環讀取源檔案内容,小循環進行壓縮寫檔案
/***************************************************************************************
* Name: file_deflate
* Desc: Compress from file source to file dest until EOF on source
* Input:
* @fname - source file name
* Output:
* Return: Z_OK on success
* Z_MEM_ERROR if memory could not be allocated for processing
* Z_STREAM_ERROR if an invalid compression level is supplied
* Z_VERSION_ERROR if the version of zlib.h and the version of
* the library linked do not match
* Z_ERRNO if there is an error reading or writing the files
* Others: -
***************************************************************************************/
int file_deflate(const char *fsrc, const char *fdst)
{
int res = -1;
int flush = Z_NO_FLUSH;
FILE *src = fopen(fsrc, "r");
FILE *dst = fopen(fdst, "w+");
assert(src && dst);
z_stream strm = {Z_NULL};
res = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
assert(res == Z_OK);
/* Compress until end of file */
do {
char in[SIZE_CHUNK];
size_t rlen = fread(in, 1, sizeof(in), src);
if (rlen == 0) {
printf("EOF\n");
break;
}
// 這裡需要區分檔案尾或者檔案中間
flush = feof(src) ? Z_FINISH : Z_NO_FLUSH;
__do_deflate_write(strm, dst, in, rlen, flush);
} while (flush != Z_FINISH);
/* clean up and return */
(void)deflateEnd(&strm);
fclose(src);
fclose(dst);
return Z_OK;
}
static void __do_deflate_write(z_stream &strm,
FILE *fp, void *data, size_t size, int flush)
{
char out[SIZE_CHUNK];
strm.avail_in = size;
strm.next_in = (Bytef *)data;
printf("deflate: %u\n", strm.avail_in);
/* run deflate() on input until output buffer not full, finish
* compression if all of source has been read in */
do {
strm.next_out = (Bytef *)out;
strm.avail_out = sizeof(out);
int res = deflate(&strm, flush);
assert(res != Z_STREAM_ERROR); // state not clobbered
size_t wlen = fwrite(out, 1, sizeof(out) - strm.avail_out, fp);
assert(wlen == sizeof(out) - strm.avail_out);
printf(" write: %zu\n", wlen);
} while (strm.avail_out == 0); // 傳回0,則表示正好out空間不夠大了
assert(strm.avail_in == 0); /* all input will be used */
}
3.3 解壓檔案
解壓檔案過程與壓縮過程大同小異
/***************************************************************************************
* Name: file_inflate
* Desc: Decompress from file source to file dest until stream ends or EOF.
* Input:
* Output:
* Return: Z_OK on success
* Z_MEM_ERROR if memory could not be allocated for processing
* Z_DATA_ERROR if the deflate data isinvalid or incomplete
* Z_VERSION_ERROR if the version of zlib.h and the version of
* the library linked do not match
* Z_ERRNO if there is an error reading or writing the files
* Others: -
***************************************************************************************/
int file_inflate(const char *fsrc, const char *fdst)
{
int res = -1;
FILE *src = fopen(fsrc, "r");
FILE *dst = fopen(fdst, "w+");
assert(src && dst);
z_stream strm = {Z_NULL};
res = inflateInit(&strm);
assert(res == Z_OK);
/* decompress until deflate stream ends or end of file */
do {
char in[SIZE_CHUNK];
size_t rlen = fread(in, 1, sizeof(in), src);
if (rlen == 0) {
printf("EOF\n");
break;
}
/* done when inflate() says it's done */
res = __do_inflate_write(strm, dst, in, rlen);
} while (res != Z_STREAM_END);
/* clean up and return */
(void)inflateEnd(&strm);
fclose(src);
fclose(dst);
return res == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
還是需要注意的就是,解壓過程也不一定是一次調用就能直接成功的。
static int __do_inflate_write(z_stream &strm,
FILE *fp, void *data, size_t size)
{
int res = -1;
char out[SIZE_CHUNK];
strm.next_in = (Bytef *)data;
strm.avail_in = size;
printf("inflate: %u\n", strm.avail_in);
/* run inflate() on input until output buffer not full */
do {
strm.avail_out = sizeof(out);
strm.next_out = (Bytef *)out;
res = inflate(&strm, Z_NO_FLUSH);
assert(res != Z_STREAM_ERROR); /* state not clobbered */
switch (res) {
case Z_NEED_DICT:
res = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR:
case Z_MEM_ERROR:
goto out;
}
size_t wlen = fwrite(out, 1, sizeof(out) - strm.avail_out, fp);
assert(wlen == sizeof(out) - strm.avail_out);
printf(" write: %zu\n", wlen);
} while (strm.avail_out == 0);
out:
return res;
}
4.運作
程式設計程式,壓縮a到b、再解壓b到c,檢視md5結果:
g++ -Wall zpipe2.c -lz
./a.out -d a.out b.out
deflate: 8192
write: 2
deflate: 8192
write: 0
deflate: 872
write: 5356
Result: [Success]
./a.out b.out c.out
inflate: 5358
write: 8192
write: 8192
write: 872
Result: [Success]
md5sum *.out
60e689ba89c8eb9732b1ed9949bcdd7d a.out
36f7876b1e3d2e446df0b5e88b43e9a9 b.out
60e689ba89c8eb9732b1ed9949bcdd7d c.out
5.結論
通過快速學習example對zlib接口進行上手,程式設計心得是壓縮、解壓縮需要封裝小函數循環進行處理,不一定是一次就能處理到所有資料的。如果檔案内容為純文字形式,壓縮率是挺高的,工程上有非常好的實用意義。