深入理解数据压缩流程及 zlib 库中相关函数

数据压缩是一种常见的操作,可以有效地减小数据的体积,节省存储空间和网络带宽。在本文中,我们将深入讨论数据压缩的流程,并详细解释 zlib 库中相关函数的使用,包括 deflateInit2()deflate()deflateEnd() 等。我们将通过一个具体的例子来展示如何使用这些函数来实现数据的压缩操作。

先来讲讲这三个最重要的函数吧。

函数作用

deflateInit2()

函数的主要作用是初始化压缩器,并设置相关的参数。在调用这个函数之后,我们就可以使用返回的 z_stream 结构体来执行压缩操作。通过设置不同的参数,我们可以定制压缩器的行为,以满足不同的压缩需求。

deflate()

函数的主要作用是执行压缩操作。在调用这个函数之前,我们需要将待压缩的数据和输出缓冲区的信息设置到 z_stream 结构体中,然后调用 deflate() 函数来执行压缩操作。压缩器将会尽可能多地将输入数据压缩到输出缓冲区中,并返回压缩操作的状态。根据返回值,我们可以判断压缩操作是否成功,并进行相应的错误处理或者下一步操作。

deflateEnd()

函数的主要作用是结束压缩器的工作,并释放相关资源。在调用这个函数之前,我们需要确保所有的压缩操作已经完成,不再需要压缩器的工作了。调用这个函数后,压缩器将释放所占用的内存空间,并结束其工作状态,不再接受新的压缩任务。

通常情况下,我们在完成所有的压缩操作后,应该调用 deflateEnd() 函数来释放压缩器所占用的资源,以确保程序的内存使用情况正常,并避免出现内存泄漏等问题。

函数接口讲解

1.deflateInit2();

int deflateInit2(z_stream *strm, int level, int method, int windowBits, int memLevel, int strategy);

参数说明:

  • strm:指向一个 z_stream 结构的指针,该结构包含了要初始化的压缩器的相关信息。
  • level:压缩级别,取值范围为 09,表示压缩程度从不压缩到最大压缩。常用的值包括 Z_DEFAULT_COMPRESSION (默认压缩级别)和 Z_NO_COMPRESSION (不压缩)。
  • method:压缩方法,通常为 Z_DEFLATED,表示使用 DEFLATE 压缩算法。
  • windowBits:窗口大小,通常取值为 -15-8,其中 -15 表示使用默认窗口大小(最大压缩),而其他值可以用于指定自定义的窗口大小。
  • memLevel:内存级别,用于控制内存的使用,取值范围为 19
  • strategy:压缩策略,包括 Z_DEFAULT_STRATEGY(默认策略)、Z_FILTERED(滤波器策略)和 Z_HUFFMAN_ONLY(仅霍夫曼编码)等。

strategy压缩策略如何选择

  1. Z_DEFAULT_STRATEGY(默认策略)

    • 这是默认的压缩策略,它在大多数情况下表现良好。
    • 它使用 zlib 库内部的启发式算法来选择最佳的压缩方法。
    • 在大多数情况下,Z_DEFAULT_STRATEGY 提供了良好的压缩比和速度。
  2. Z_FILTERED(滤波器策略)

    • 滤波器策略主要适用于数据流中包含重复模式的情况,例如,图像数据或者音频数据。
    • 这个策略使用了更复杂的滤波器算法,以便更好地识别和利用数据中的重复模式。
    • 滤波器策略通常可以获得更好的压缩比,但可能会导致压缩速度变慢。
  3. Z_HUFFMAN_ONLY(仅霍夫曼编码)

    • 这个策略强制使用霍夫曼编码来压缩数据,而不使用其他压缩算法。
    • 霍夫曼编码通常在文本数据或者其他具有高度可预测性的数据上表现良好,但在一般情况下可能不如其他算法那样有效。
    • Z_HUFFMAN_ONLY 策略通常会牺牲一些压缩比以换取更快的压缩速度。

综合来说,选择合适的压缩策略取决于应用场景和数据特点。对于大多数通用数据,Z_DEFAULT_STRATEGY 是一个良好的选择。如果数据中存在明显的重复模式,可以考虑使用 Z_FILTERED 策略来获得更好的压缩效果。而 Z_HUFFMAN_ONLY 策略则适用于特定类型的数据,例如文本数据。

2.int deflate();

int deflate(z_stream *strm, int flush);

参数说明:

  1. strm:指向一个 z_stream 结构体的指针,包含了压缩器的相关信息,包括输入数据和输出缓冲区等。
  2. flush:操作标志,用于指定执行压缩操作的方式。可选的值有:
    • Z_NO_FLUSH:普通压缩操作,压缩器将尽可能多地将输入数据压缩到输出缓冲区中,但不保证输出缓冲区被填满。
    • Z_SYNC_FLUSH:同步刷新操作,压缩器将立即将尽可能多的输入数据压缩到输出缓冲区中,并且保证输出缓冲区被填满。
    • Z_FULL_FLUSH:完全刷新操作,类似于 Z_SYNC_FLUSH,但同时清空压缩器的内部状态,使得压缩后的数据可以独立解压缩。
    • Z_FINISH:结束操作,表示输入数据已经全部传递给压缩器,压缩器在压缩完成后返回。

返回值:

deflate() 函数返回一个整数值,表示压缩操作的状态。可能的返回值有:

  • Z_OK:表示压缩操作成功。
  • Z_STREAM_END:表示输入数据已经被完全压缩,并且压缩器处于结束状态。
  • Z_BUF_ERROR:表示输出缓冲区不足,需要更大的缓冲区才能继续执行压缩操作。
  • Z_STREAM_ERROR:表示压缩器内部状态错误,通常是由于参数设置不正确或者其他原因造成的。

3.deflateEnd()

int deflateEnd(z_stream *strm);

参数说明:

strm:指向一个 z_stream 结构体的指针,用于传递压缩器的相关信息。

返回值:

deflateEnd() 函数返回一个整数值,表示结束压缩器工作的状态。可能的返回值有:

  • Z_OK:表示压缩器工作结束成功,相关资源已被释放。
  • Z_STREAM_ERROR:表示压缩器内部状态错误,通常是由于参数设置不正确或者其他原因造成的。

数据压缩流程概述

数据压缩的流程一般包括以下几个步骤:

  1. 设置压缩器参数:初始化压缩器并设置相关的参数,包括压缩级别、压缩方法、窗口大小等。
  2. 传递待压缩数据:将待压缩的数据传递给压缩器。
  3. 执行压缩:执行压缩操作,并将压缩后的数据写入输出缓冲区。
  4. 写入压缩后的数据:将压缩后的数据写入到输出文件或输出流中。
  5. 结束压缩:释放压缩器所占用的资源,并结束压缩操作。

在下面的示例中,我们将详细讨论每个步骤,并演示如何使用 zlib 库中的函数来实现这些操作。

2. 流程详解

2.1. 设置压缩器参数

在使用 zlib 库进行数据压缩之前,我们需要初始化一个压缩流 z_stream,并设置相关的参数。下面是设置压缩器参数的代码示例:

z_stream zs;
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_FILTERED) != Z_OK)
{std::cout << "Failed to initialize zlib deflate!" << std::endl;// 处理初始化失败的情况
}

在这段代码中,我们初始化了一个 z_stream 结构体,并将其相关参数设置为 Z_NULL。然后,我们使用 deflateInit2() 函数初始化压缩器,并设置了压缩级别、压缩方法、窗口大小等参数。如果初始化失败,函数将返回 Z_OK,我们需要进行错误处理。

1. zs.zalloc

zs.zalloc 是一个指向内存分配函数的指针。当 zlib 需要分配内存来存储压缩或解压缩所需的数据结构时,它会调用这个指针指向的函数来分配内存。通常情况下,我们可以将这个指针设置为 Z_NULL,表示使用默认的内存分配函数(即 malloc() 函数)。如果需要使用自定义的内存分配函数,我们可以将这个指针设置为指向我们自定义的分配函数。

2. zs.zfree

zs.zfree 是一个指向内存释放函数的指针。当 zlib 需要释放之前分配的内存时,它会调用这个指针指向的函数来释放内存。通常情况下,我们可以将这个指针设置为 Z_NULL,表示使用默认的内存释放函数(即 free() 函数)。如果需要使用自定义的内存释放函数,我们可以将这个指针设置为指向我们自定义的释放函数。

3. zs.opaque

zs.opaque 是一个指向透明对象的指针,用于传递额外的参数给内存分配和释放函数。当 zlib 调用内存分配和释放函数时,它会将 zs.opaque 指针指向的对象传递给这些函数,以便我们可以在函数中访问和使用这些额外的参数。通常情况下,我们可以将这个指针设置为 Z_NULL,表示不需要传递额外的参数。如果需要传递额外的参数给内存分配和释放函数,我们可以将这个指针设置为指向我们所需的对象。

2.2. 传递待压缩数据

接下来,我们需要将待压缩的数据传递给压缩器。这里我们将使用 zs.next_in 指针来指向待压缩的数据,并使用 zs.avail_in 来指定待压缩数据的大小。下面是传递待压缩数据的代码示例:

zs.next_in = (Bytef *)body.c_str();
zs.avail_in = body.size();

在这段代码中,我们将待压缩的数据的指针和大小设置到 z_stream 结构体中,以便压缩器可以对其进行压缩操作。

具体讲解:

  1. zs.next_in = (Bytef *)body.c_str();

    • zs.next_in 是指向将要被压缩的数据的指针。
    • body.c_str() 返回一个指向字符串 body 存储的字符数组的指针,类型为 const char *
    • (Bytef *) 这部分是类型转换,将 const char * 转换为 Bytef * 类型,Bytef 类型是 zlib 库定义的无符号字节类型,用于表示字节数据。
    • 这样,zs.next_in 就指向了 body 字符串的首地址,即要被压缩的数据的起始位置。
  2. zs.avail_in = body.size();

    • zs.avail_in 表示待压缩的数据的字节大小。
    • body.size() 返回字符串 body 中字符的数量,即待压缩的数据的大小。
    • 这样,zs.avail_in 就设置为了待压缩数据的字节大小。

这两行代码的作用是将待压缩的数据的指针和大小设置到压缩流结构体 z_stream 中,以便后续的压缩操作可以正确地处理这些数据。

2.3. 执行压缩

一旦设置了压缩器的参数和输入数据,我们就可以执行压缩操作了。我们使用 deflate() 函数来执行压缩操作,并将压缩后的数据写入输出缓冲区。下面是执行压缩操作的代码示例:

char outbuffer[32768];
//这两行代码用于设置 zlib 压缩流 z_stream 的输出参数
zs.next_out = reinterpret_cast<Bytef *>(outbuffer);
zs.avail_out = sizeof(outbuffer);if (deflate(&zs, Z_FINISH) == Z_STREAM_ERROR)
{std::cout << "Failed to deflate data!" << std::endl;// 处理压缩失败的情况
}

在这段代码中,我们准备了一个输出缓冲区 outbuffer,并将其地址设置到 zs.next_out 指针中。然后,我们指定输出缓冲区的大小到 zs.avail_out 中,并使用 deflate() 函数执行压缩操作。如果压缩操作失败,函数将返回 Z_STREAM_ERROR,我们需要进行错误处理。

具体讲解:

  1. zs.next_out = reinterpret_cast<Bytef *>(outbuffer);

    • zs.next_out 是指向用于存储压缩后数据的缓冲区的指针。
    • reinterpret_cast<Bytef *>(outbuffer)outbuffer 缓冲区的地址转换为 Bytef * 类型,Bytef 类型是 zlib 库定义的无符号字节类型,用于表示字节数据。
    • 这样,zs.next_out 就指向了用于存储压缩后数据的缓冲区的起始位置。
  2. zs.avail_out = sizeof(outbuffer);

    • zs.avail_out 表示输出缓冲区的大小,即缓冲区可用于存储的字节数。
    • sizeof(outbuffer) 返回 outbuffer 缓冲区的大小,即缓冲区的总容量。
    • 这样,zs.avail_out 就设置为了输出缓冲区的大小。

这两行代码的作用是将用于存储压缩后数据的缓冲区的指针和大小设置到压缩流结构体 z_stream 中,以便 deflate() 函数可以将压缩后的数据写入到这个缓冲区中。

2.4. 写入压缩后的数据

压缩操作完成后,我们将压缩后的数据写入到输出文件或输出流中。在这个示例中,我们将压缩后的数据写入到输出文件中。下面是写入压缩后的数据的代码示例:

ofs.write(outbuffer, sizeof(outbuffer) - zs.avail_out);

在这段代码中,我们使用 write() 函数将压缩后的数据写入到输出文件中。我们从输出缓冲区中写入数据的大小是 sizeof(outbuffer) - zs.avail_out,即实际压缩后数据的大小。

2.5. 结束压缩

最后,我们需要结束压缩器的工作,并释放相关资源。我们使用 deflateEnd() 函数来结束压缩器的工作,释放压缩器所占用的内存空间。下面是结束压缩器工作的代码示例:

deflateEnd(&zs);

在这段代码中,我们调用 deflateEnd() 函数结束压缩器的工作,并传入 z_stream 结构体的指针作为参数。这样,压缩器就会释放所占用的资源,并结束压缩操作。

具体讲解:

deflateEnd() 函数用于结束压缩器的工作,并释放相关资源。这个函数会清理 z_stream 结构体和与之关联的内存空间,以及释放压缩所需的其他资源。具体来说,deflateEnd() 函数会完成以下操作:

  • 清理和释放与给定的 z_stream 结构体相关联的压缩状态。
  • 释放任何由压缩器分配的内存,例如压缩器使用的内部缓冲区。
  • 结束压缩器的工作并释放相关资源,使得压缩器可以被重新初始化或销毁。

在使用 deflate() 函数完成所有压缩操作后,应该调用 deflateEnd() 函数来释放压缩器所占用的资源。这样可以确保程序在完成压缩任务后正确释放内存,并避免内存泄漏等问题。

实现一个压缩函数的完整API实例

        bool SetCompressedContent(const std::string &body){std::ofstream ofs;ofs.open(_name + ".gz", std::ios::binary); // 以二进制写入模式打开压缩文件if (!ofs.is_open()){std::cout << "Failed to open compressed file!" << std::endl;return false;}z_stream zs;zs.zalloc = Z_NULL;zs.zfree = Z_NULL;zs.opaque = Z_NULL;if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_FILTERED) != Z_OK){std::cout << "Failed to initialize zlib deflate!" << std::endl;ofs.close();return false;}zs.next_in = (Bytef *)body.c_str();zs.avail_in = body.size();char outbuffer[32768];do{zs.next_out = reinterpret_cast<Bytef *>(outbuffer);zs.avail_out = sizeof(outbuffer);if (deflate(&zs, Z_FINISH) == Z_STREAM_ERROR){std::cout << "Failed to deflate data!" << std::endl;deflateEnd(&zs);ofs.close();return false;}ofs.write(outbuffer, sizeof(outbuffer) - zs.avail_out);} while (zs.avail_out == 0);deflateEnd(&zs);ofs.close();return true;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/733497.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 客户端向服务端上传文件(TCP通信)

一、实验内容 编写一个客户端向服务端上传文件的程序&#xff0c;要求使用TCP通信的的知识&#xff0c;完成将本地机器输入的路径下的文件上传到D盘中名称为upload的文件夹中。并把客户端的IP地址加上count标识作为上传后文件的文件名&#xff0c;即IP&#xff08;count&#…

OpenSearch 与 Elasticsearch:哪个开源搜索引擎适合您?

当谈论到搜索引擎产品时&#xff0c;Elasticsearch 和 OpenSearch 是两个备受关注的选择。它们都以其出色的功能和灵活性而闻名&#xff0c;但在一些方面存在一些差异。在本文中&#xff0c;我们将从功能和延展性、工具与资源、价格和许可这三个角度对这两个产品进行论述。通过…

qt+opencv人脸人眼检测识别

项目运行涉及到opencv库&#xff0c;以及haarcascade_frontalface_default.xml和haarcascade_eye_tree_eyeglasses.xml。qt配置opencv可见先前文章&#xff0c;另外这两份OpenCV 中用于眼睛检测的级联分类器xml文件&#xff0c;是我在网上下载的。 把要使用到的文件都放到当前…

鸿蒙培训开发:就业市场的新热点~

金三银四在即&#xff0c;随着春节假期结束&#xff0c;各行各业纷纷复工复产&#xff0c;2024年的春季招聘市场也迎来了火爆的局面。最近发布的《2024年春招市场行情周报&#xff08;第一期&#xff09;》显示&#xff0c;尽管整体就业市场仍处于人才饱和状态&#xff0c;但华…

spring-cloud-openfeign 3.0.0(对应spring boot 2.4.x之前版本)之前版本feign整合ribbon请求流程

在之前写的文章配置基础上 https://blog.csdn.net/zlpzlpzyd/article/details/136060312 下图为自己整理的

物联网与边缘计算的结合

目录 一、实时响应与决策 二、降低网络带宽需求和传输延迟 三、隐私保护与数据安全 四、系统可靠性与稳定性 总结 物联网与边缘计算的结合&#xff1a;为未来的智能化应用注入强大动力 随着科技的飞速发展&#xff0c;物联网与边缘计算的结合已经成为推动各行各业创新发展…

Excel 快速填充/输入内容

目录 一. Ctrl D/R 向下/右填充二. 批量输入内容 一. Ctrl D/R 向下/右填充 ⏹如下图所示&#xff0c;通过快捷键向下和向右填充数据 &#x1f914;当选中第一个单元格之后&#xff0c;可以按住Shift后&#xff0c;再选中最后一个单元格&#xff0c;可以选中第一个单元格和最…

python常识系列24-->python操作mysql之pymysql

前言 pymsql是Python中操作MySQL的模块程序在运行时&#xff0c;数据都是在内存中的。当程序终止时&#xff0c;通常需要将数据保存在磁盘上。 安装模块 pip install PyMySql基本使用 ## 使用 connect 函数创建连接对象&#xff0c;此连接对象提供关闭数据库、事务回滚等操…

自动驾驶技术解析与关键步骤

目录 前言1 自动驾驶主要技术流程1.1 车辆周围环境感知1.2 车辆和行人检测分析1.3 运动轨迹规划 2 关键技术概述2.1 车辆探测与图片输入2.2 行人检测2.3 运动规划2.4 电子地图2.5 轨迹预测2.6 交通灯分析2.7 故障检测 结语 前言 自动驾驶汽车作为未来交通领域的重要发展方向&a…

【Python】-入门:安装配置和IDLE的使用

Python的安装和配置 一、下载Python安装包 首先&#xff0c;你需要从Python的官方网站&#xff08;https://www.python.org/downloads/&#xff09;下载适合你操作系统的Python安装包。请注意&#xff0c;Python 2.x版本即将停止维护&#xff0c;因此推荐下载Python 3.x版本。…

不同框架表示图像时维度顺序的区别:pytorch、kerastf、opencv、numpy、PIL

在PyTorch、Keras、OpenCV、NumPy和PIL这几个框架中&#xff0c;它们在表示图像时的维度存储顺序有所不同。下面我将逐一解释每个框架中图像维度的存储顺序&#xff1a; 1&#xff0c;PyTorch: PyTorch中图像的维度顺序通常遵循 [N, C, H, W] 的格式&#xff0c;也就是channe…

【LGR-176-Div.2】[yLCPC2024] 洛谷 3 月月赛 I(A~C and G<oeis>)

[yLCPC2024] A. dx 分计算 前缀和提前处理一下区间和&#xff0c;做到O&#xff08;1&#xff09;访问就可以过。 #include <bits/stdc.h> //#define int long long #define per(i,j,k) for(int (i)(j);(i)<(k);(i)) #define rep(i,j,k) for(int (i)(j);(i)>(k);…

Redis作为缓存的数据一致性问题

背景 使用Reids作为缓存的原因&#xff1a; 在高并发场景下&#xff0c;传统关系型数据库的并发能力相对比较薄弱&#xff08;QPS不能太大&#xff09;&#xff1b; 使用Redis做一个缓存。让用户请求先打到Redis上而不是直接打到数据库上。 但是如果出现数据更新操作&#xff…

【C/C++ 学习笔记】运算符

【C/C 学习笔记】运算符 视频地址: Bilibili 算术运算符 运算符含义备注 加号 − - −减号 ∗ * ∗乘号 / / /除号整数相除结果依然是整数&#xff08;直接舍去小数部分&#xff09;&#xff0c;小数相除还是小数 % 取模小数无法进行取模运算&#xff1b;对 0 取模会报错 …

Windows下同一电脑配置多个Git公钥访问不同的账号

前言 产生这个问题的原因是我在Gitee码云上有两个账号,为了方便每次不用使用http模式推拉代码,于是我就使用了ssh的模式,起初呢我用两台电脑分别连接两个账号,用起来也相安无事,近段时时间台式机在家里,我在外地出差了,就想着把ssh公钥同时添加到不同的账号里,结果却发现不能用…

超网、IP 聚合、IP 汇总分别是什么?三者有啥区别和联系?

一、超网 超网&#xff08;Supernet&#xff09;是一种网络地址聚合技术&#xff0c;它可以将多个连续的网络地址合并成一个更大的网络地址&#xff0c;从而减少路由表的数量和大小。超网技术可以将多个相邻的网络地址归并成一个更大的网络地址&#xff0c;这个更大的网络地址…

【Vue3 组合式 API: reactive 和 ref 函数】

文章目录 1. 什么是组合式 API&#xff1f;2. reactive 函数3. ref 函数4. reactive vs ref 1. 什么是组合式 API&#xff1f; 组合式 API 是 Vue 3 中的一种新特性&#xff0c;它允许我们通过函数来组织组件的逻辑&#xff0c;而不是依赖于选项式 API 中的选项对象。这使得代…

Git使用教程:入门到精通

Git使用教程&#xff1a;入门到精通 一、Git安装根据需求选择电脑位数安装&#xff1b;20231023210945建议这里先新建一个文件夹如&#xff1a;D:/Git&#xff1b;专门来存放Git安装包和后续Git代码&#xff0c;方便管理&#xff1b; 二、Git使用前的配置需要先创建自己的Gitee…

贪心算法(蓝桥杯 C++ 题目 代表 注解)

介绍&#xff1a; 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在每一步选择中都采取当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望最终能够得到全局最好或最优的结果的算法。它通常用来解决一些最优化问题&#xff0c;如最小生…

❤ Vue3项目搭建系统篇(二)

❤ Vue3项目搭建系统篇&#xff08;二&#xff09; 1、安装和配置 Element Plus&#xff08;完整导入&#xff09; yarn add element-plus --savemain.ts中引入&#xff1a; // 引入组件 import ElementPlus from element-plus import element-plus/dist/index.css const ap…