Linux下使用zlib实现文件压缩解压

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接口进行上手,编程心得是压缩、解压缩需要封装小函数循环进行处理,不一定是一次就能处理到所有数据的。如果文件内容为纯文本形式,压缩率是挺高的,工程上有非常好的实用意义。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值