最实际标准是用zlib的gzopen/gzwrite/gzread系列函数处理gzip文件,必须用"wb"/"rb"模式、检查返回值、配合gzeof判断结束、注意Windows中文路径需转宽字符。

用 zlib 做 gzip 压缩/解压最实际
标准 C++ 没有内置文件压缩能力,zlib 是事实上的跨平台首选——不是因为它多高级,而是因为几乎所有系统都预装或容易编译,且 gzip 格式兼容性极好。别碰 boost::iostreams 的压缩 filter,配置麻烦、报错晦涩、Windows 下常连不上静态库。
实操建议:
- Linux/macOS 直接
sudo apt install zlib1g-dev或brew install zlib;Windows 用 vcpkg:vcpkg install zlib:x64-windows - 压缩单个文件时,不要试图用
gzopen直接写二进制流——它默认按文本模式处理换行,导致解压后文件损坏;必须用"wb"模式打开gzFile,并确保源数据以原始字节读入 -
gzwrite返回值必须检查:返回值小于写入长度说明出错,常见原因是磁盘满或权限不足,但错误码藏在gzerror里,不调用就只能看到 -1
std::filesystem + zlib 打包多个文件要绕过目录结构限制
zlib 本身不支持 ZIP 多文件+目录元信息,它只处理单流压缩。想“压缩一个文件夹”,本质是自己拼 tar 流再 gzip——但别手写 tar header,太容易字节对齐出错。更稳的做法是调用外部 tar 命令生成 .tar,再用 zlib 压成 .tar.gz。
关键点:
立即学习“C++免费学习笔记(深入)”;
- 用
std::filesystem::recursive_directory_iterator遍历路径时,注意path().lexically_relative(base)获取相对路径,否则 tar header 里的文件名会带盘符(Windows)或绝对路径(Linux),解压时可能覆盖系统文件 - 生成 tar 流时,每个文件块必须是 512 字节对齐,末尾用
\0填充;少填或多填都会导致tar -xzf报truncated header - 如果坚持纯 C++ 不调外部命令,可用
libarchive替代zlib,它原生支持 ZIP/tar/gzip 多格式,但体积大、依赖多,小工具没必要
解压时 gzread 返回 0 不代表结束,得看 gzeof
很多代码把 gzread(gzfp, buf, size) == 0 当作 EOF,这是错的——当缓冲区满但流还没完,或刚好读到数据末尾时,gzread 都可能返回 0。真正判断是否结束,必须配合 gzeof(gzfp)。
典型陷阱:
- 循环里只靠
gzread返回值决定是否继续,会导致解压提前终止,尤其处理大文件时概率更高 - 解压目标是二进制文件(如图片、PDF)时,用
fputs写入会截断\0字节,必须用fwrite(buf, 1, n, fp) - 如果源 gzip 文件被截断,
gzread可能返回正数但后续调用失败,此时gzerror会返回Z_DATA_ERROR,需主动检查
Windows 下 gzopen 路径含中文会失败
zlib 的 gzopen 底层调用的是 CRT 的 fopen,而 Windows 的 fopen 默认不支持 UTF-8 路径(即使编译器设了 UTF-8 源码)。传入含中文的 const char*,实际被当作了本地 ANSI 编码,一读就返回 NULL。
绕过方法只有两个:
- 用
MultiByteToWideChar转成宽字符,再通过_wfopen打开文件,然后用fdopen把FILE*绑给gzopen(传"rb"模式) - 更简单:先用
std::filesystem::u8path得到 UTF-8 字符串,再通过CreateFileW以CREATE_ALWAYS打开句柄,用_open_osfhandle转成 C runtime fd,最后fdopen+gzdopen - 别尝试设置
_setmode(_fileno(stdout), _O_U16TEXT),这不影响zlib内部 fopen 行为
压缩逻辑本身不复杂,难的是边界情况:路径编码、流状态判断、内存对齐、错误码解析。这些地方一漏,程序在某台机器上静默失败,比崩溃还难查。


















