如何用zlib的deflate算法在内存中将缓冲区压缩至char数组?
实现zlib内存中压缩缓冲区到char数组
我明白你想要跳过文件IO,直接把zlib压缩的结果存到内存中的char数组里——这完全可行,只需要调整zlib的输出逻辑,用动态内存容器替代文件操作就行。下面是针对你的代码修改后的完整实现思路和代码:
核心修改点
- 移除所有文件相关的操作(
fopen/fwrite/fclose),改用**std::vector<char>**来动态存储压缩后的数据(它能自动扩容,避免固定数组大小不够的问题)。 - 调整
Write和Close方法,把原本写入文件的逻辑替换成向内存容器追加数据。 - 补充必要的初始化逻辑,确保zlib流正确指向内存缓冲区。
首先更新类成员变量
在你的File类里,去掉文件指针m_pFile,添加一个动态内存容器来存压缩结果:
#include <vector> // ... class File { private: z_stream m_Stream; bool m_bWrite; Bytef m_Buffer[4096]; // 推荐用4KB/8KB的缓冲区,zlib官方建议的大小 std::vector<char> m_CompressedData; // 存储最终压缩结果 // ... 其他成员 public: // 新增获取压缩数据的接口 const char* GetCompressedData() const { return m_CompressedData.empty() ? nullptr : m_CompressedData.data(); } size_t GetCompressedSize() const { return m_CompressedData.size(); } // ... 其他方法 };
初始化压缩流(新增方法)
在开始压缩前,需要正确初始化zlib流和内存缓冲区:
bool File::OpenForCompression() { m_bWrite = true; memset(&m_Stream, 0, sizeof(m_Stream)); // 初始化zlib压缩流,使用默认压缩级别 int err = deflateInit(&m_Stream, Z_DEFAULT_COMPRESSION); if (err != Z_OK) { // 这里可以添加错误日志或处理逻辑 return false; } // 绑定输出缓冲区到内存数组 m_Stream.next_out = m_Buffer; m_Stream.avail_out = sizeof(m_Buffer); m_CompressedData.clear(); // 清空之前的数据 return true; }
修改Write方法(内存版)
把原本写入文件的逻辑改成向std::vector追加数据:
int File::Write(const void *pBuffer, int nByte) { if (!m_bWrite) return 0; m_Stream.next_in = (Bytef*)pBuffer; m_Stream.avail_in = (uInt)nByte; int err = deflate(&m_Stream, Z_NO_FLUSH); while (m_Stream.avail_out == 0) { // 把当前缓冲区的所有数据追加到内存容器 m_CompressedData.insert(m_CompressedData.end(), reinterpret_cast<char*>(m_Buffer), reinterpret_cast<char*>(m_Buffer) + sizeof(m_Buffer)); // 重置输出缓冲区,继续接收压缩数据 m_Stream.avail_out = sizeof(m_Buffer); m_Stream.next_out = m_Buffer; err = deflate(&m_Stream, Z_NO_FLUSH); if (err != Z_OK) { // 压缩出错,返回0表示失败 return 0; } } return nByte; }
修改Close方法(完成压缩并收尾)
处理压缩流的收尾,把剩余的数据也存入内存容器:
bool File::Close() { if(m_bWrite) { int err; // 完成压缩流,写入所有剩余数据 for (;;) { err = deflate(&m_Stream, Z_FINISH); // 计算缓冲区中已使用的字节数,追加到内存容器 size_t usedBytes = sizeof(m_Buffer) - m_Stream.avail_out; if (usedBytes > 0) { m_CompressedData.insert(m_CompressedData.end(), reinterpret_cast<char*>(m_Buffer), reinterpret_cast<char*>(m_Buffer) + usedBytes); } if (err == Z_STREAM_END) break; // 压缩完成 if (err != Z_OK) { CHECK_ERR(err, "deflate"); deflateEnd(&m_Stream); return false; } // 重置缓冲区继续接收收尾数据 m_Stream.avail_out = sizeof(m_Buffer); m_Stream.next_out = m_Buffer; } // 结束压缩流 err = deflateEnd(&m_Stream); if (err != Z_OK) { CHECK_ERR(err, "deflateEnd"); return false; } m_bWrite = false; } else { // 如果是解压模式,同样可以类似修改为内存解压,这里保留你的原逻辑(如果需要) int err = inflateEnd(&m_Stream); // 移除fclose,因为不再操作文件 CHECK_ERR(err, "inflateEnd"); } return true; }
使用示例
File compFile; if (compFile.OpenForCompression()) { const char* rawData = "你的原始缓冲区数据"; int rawSize = strlen(rawData); compFile.Write(rawData, rawSize); compFile.Close(); // 获取压缩后的数据和大小 const char* compressed = compFile.GetCompressedData(); size_t compressedSize = compFile.GetCompressedSize(); // 这里就可以使用compressed指向的内存数组了 }
注意事项
- 缓冲区
m_Buffer的大小建议设置为4KB或8KB,这是zlib官方推荐的最优大小,能平衡内存占用和压缩效率。 - 确保错误处理逻辑完善,zlib的每个操作都可能返回错误码,需要及时处理避免内存泄漏或数据丢失。
- 如果需要兼容C风格的char数组,可以通过
GetCompressedData()直接获取指针,或者把std::vector的数据复制到你自己的数组中。
内容的提问来源于stack exchange,提问作者yjtu
相关产品推荐
相关产品推荐

