天天看點

Linux下使用zlib實作檔案壓縮解壓1.背景2.思路3.實作4.運作5.結論

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接口進行上手,程式設計心得是壓縮、解壓縮需要封裝小函數循環進行處理,不一定是一次就能處理到所有資料的。如果檔案内容為純文字形式,壓縮率是挺高的,工程上有非常好的實用意義。

繼續閱讀