zlib的简单使用

1.介绍

zlib 是一个广泛使用的数据压缩库,它提供了内存中的压缩和解压缩功能,并能够检查解压数据的完整性。zlib 支持读写 gzip (.gz) 格式的文件,并且默认使用 deflate 算法进行数据压缩,这是一种增强的 Huffman 编码算法。

2.安装

zlib Home Site

3.一次性读入内存解压缩compress和uncompress

#include <stdio.h>
#include "zlib.h"

int main()
{
    /* 原始数据 */
    unsigned char strSrc[] = "hello world!中文测试 yes";
    unsigned char buf[1024] = {0};
    unsigned char strDst[1024] = {0};
    unsigned long srcLen = sizeof(strSrc);
    unsigned long bufLen = sizeof(buf);
    unsigned long dstLen = sizeof(strDst);

    printf("Src string:%s\nLength:%ld\n", strSrc, srcLen);

    /* 压缩 */
    compress(buf, &bufLen, strSrc, srcLen);
    printf("After Compressed Length:%ld\n", bufLen);

    /* 解压缩 */
    uncompress(strDst, &dstLen, buf, bufLen);
    printf("After UnCompressed Length:%ld\n",dstLen);

    printf("UnCompressed String:%s\n",strDst);

    return 0;
}

示例解压缩一段字符串,对文件使用这两个函数时,也是一次性在内存中进行解压缩,适用于小文件,大文件应采用分块流式解压缩。

4.分块解压缩(流式)

官方示例 zlib 使用示例

#include <iostream>
#include <vector>
#include <fstream>
#include <zlib.h>
#include <spdlog/spdlog.h>
#include <stdio.h>

bool compress_data(const std::string& input_file_name, const std::string& output_file_name){
    std::ifstream input_file(input_file_name, std::ios::binary);
    if (!input_file) {
        spdlog::error("Failed to open input file: {}", input_file_name);
        return false;
    }

    std::ofstream output_file(output_file_name, std::ios::binary);
    if (!output_file) {
        spdlog::error("Failed to open output file: {}", output_file_name);
        return false;
    }
    // 设置压缩参数zstream
    z_stream strm;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    // 初始化压缩流
    if(deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK){
        spdlog::error("Failed to initialize deflate stream");
        return false;
    }
    // 读取输入文件并压缩数据
    const int buffer_size = 16 * 1024;
    std::vector<char> buffer(buffer_size);
    do {
        input_file.read(buffer.data(), buffer_size);

        strm.avail_in = static_cast<uInt>(input_file.gcount());  //读到的字节数
        strm.next_in = reinterpret_cast<Bytef*>(buffer.data());  //输入缓冲区
        do {
            std::vector<char> output_buffer(buffer_size);
            strm.avail_out = buffer_size;
            strm.next_out = reinterpret_cast<Bytef*>(output_buffer.data());
            // 压缩数据
            int result = deflate(&strm, input_file.eof() ? Z_FINISH : Z_NO_FLUSH);     //是否到文件末尾
            if (result == Z_STREAM_ERROR){
                spdlog::error("Error during compression: {}", strm.msg);
                deflateEnd(&strm);
                return false;
            }
            //加密


            
            // 写入压缩后的数据到输出文件   发送到网络
            std::streamsize compressed_size = buffer_size - strm.avail_out;
            output_file.write(output_buffer.data(), compressed_size);
        } while(strm.avail_out == 0);
    } while(!input_file.eof());
    // 压缩结束
    deflateEnd(&strm);

    input_file.close();
    output_file.close();

    return true;
}
 
bool decompress_data(const std::string& input_file_name, const std::string& output_file_name){
    std::ifstream input_file(input_file_name, std::ios::binary);
    if (!input_file) {
        spdlog::error("Failed to open input file: {}", input_file_name);
        return false;
    }

    std::ofstream output_file(output_file_name, std::ios::binary);
    if(!output_file){
        spdlog::error("faild to open output file: {}", output_file_name);
    }
    // 设置解压缩参数
    z_stream strm;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;

    //初始化解压缩流
    if(inflateInit(&strm) != Z_OK){
        spdlog::error("Failed to initialize inflate stream");
        return false;
    }

    const int buffer_size = 16 * 1024;
    std::vector<char> buffer(buffer_size);
    do {
        input_file.read(buffer.data(), buffer_size);
        //格式转换
        strm.avail_in = static_cast<uInt>(input_file.gcount());
        strm.next_in = reinterpret_cast<Bytef*>(buffer.data());
        
        do {
            std::vector<char> output_file_buffer(buffer_size);
            strm.avail_out = buffer_size;
            strm.next_out = reinterpret_cast<Bytef*>(output_file_buffer.data());
            //解压缩数据
            int result = inflate(&strm, input_file.eof() ? Z_FINISH : Z_NO_FLUSH);
            if (result == Z_STREAM_ERROR){
                spdlog::error("Error during decompression: {}", strm.msg);
                inflateEnd(&strm);
                return false;
            }
            //写入解压缩后的数据到输出文件
            std::streamsize uncompressed_size = buffer_size - strm.avail_out;
            output_file.write(output_file_buffer.data(), uncompressed_size);

        } while(strm.avail_out == 0);
    } while(!input_file.eof());

    //解压缩结束
    inflateEnd(&strm);

    input_file.close();
    output_file.close();

    return true;
}

int main(){
    std::string input_file_name = "/root/test1/OIP-C.jpg";
    std::string compressed_file_name = "/root/test1/compressed.bin";
    std::string decompressed_file_name = "/root/test1/decompressed.jpg";

    if(compress_data(input_file_name, compressed_file_name)){
        spdlog::info("Data compressed successfully.");
    }
    else {
        spdlog::error("Failed to compress data.");
        return 1;
    }

    if(decompress_data(compressed_file_name, decompressed_file_name)){
        spdlog::info("Data decompressed successfully.");
    }
    else {
        spdlog::error("Failed to decompress data.");
        return 1;
    }
    return 0;
}

5.压缩的流程

初始化结构体z_stream

初始化一个 z_stream 结构体 strm,并将一些字段设置为默认值:
zalloc 和 zfree 设置为 Z_NULL,表示使用默认的内存分配和释放函数。
opaque 设置为 Z_NULL,表示没有附加的数据。
avail_in 设置为 0,表示输入缓冲区中可用的数据长度为 0。
next_in 设置为 Z_NULL,表示输入缓冲区的指针为 NULL。

初始化压缩流(deflateInit 函数)

int deflateInit(z_stream strm, int level);*

strm:指向 z_stream 结构体的指针。
level:压缩级别,范围从 Z_NO_COMPRESSION 到 Z_BEST_COMPRESSION。

使用 deflateInit 函数初始化压缩流,并检查初始化是否成功。如果初始化失败,则记录错误信息并返回 false。

开始压缩 (deflate函数)

int deflate(z_stream strm, int flush);*

strm:指向 z_stream 结构体的指针。
flush:刷新标志,可以是 Z_NO_FLUSH、Z_PARTIAL_FLUSH、Z_SYNC_FLUSH、Z_FULL_FLUSH 或 Z_FINISH。

外部的 do-while 循环会一直执行,直到输入文件 input_file 读取完毕。

内部的 do-while 循环用于处理每次压缩操作。
初始化一个输出缓冲区 output_buffer,大小与输入缓冲区相同。
设置 avail_out 和 next_out 来指向输出缓冲区。
使用 deflate 函数进行压缩操作。如果当前是文件末尾,则传入 Z_FINISH,否则传入 Z_NO_FLUSH。
检查压缩结果是否为 Z_STREAM_ERROR,如果是则记录错误信息并结束压缩流。
计算实际压缩的字节数 compressed_size,并将其写入输出文件 output_file

结束释放资源(deflateEnd)

6.解压流程

解压也是类似的操作,不在赘述。稍加修改就能应用于网络传输,各种文件解压缩等。

posted @ 2025-07-17 14:42  桂洛克船长  阅读(721)  评论(0)    收藏  举报