zstd是facebook开发的高性能压缩工具,它只能压缩和解压单个文件,不是很方便,zip压缩工具可以将多个文件、目录压缩到一个文件,非常方便,
但zip压缩工具默认使用zlib算法,它不如zstd算法的压缩速度快,而两者的压缩率接近,libzip已经支持了zstd算法,所以让DeepSeek来完成这个工作。
压缩示例代码1:压缩内存中英文字符串
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zip.h>
// 要压缩的示例文件内容
const char *file_content = "This is some text content that will be compressed using the powerful Zstandard algorithm!\n";
int main() {
const char *zip_filename = "example_zstd.zip";
const char *file_name_in_zip = "example.txt";
int error = 0;
struct zip *archive;
struct zip_source *source;
// 1. 创建一个新的 ZIP 归档文件
archive = zip_open(zip_filename, ZIP_CREATE | ZIP_TRUNCATE, &error);
if (archive == NULL) {
zip_error_t zerror;
zip_error_init_with_code(&zerror, error);
fprintf(stderr, "Failed to create archive '%s': %s\n", zip_filename, zip_error_strerror(&zerror));
zip_error_fini(&zerror);
return EXIT_FAILURE;
}
// 2. 创建一个数据源(这里从内存缓冲区创建)
source = zip_source_buffer(archive, file_content, strlen(file_content), 0);
if (source == NULL) {
fprintf(stderr, "Failed to create source buffer: %s\n", zip_strerror(archive));
zip_close(archive);
return EXIT_FAILURE;
}
// 3. !!!关键步骤:向归档中添加文件,并指定压缩方法为 Zstd
zip_int64_t index = zip_file_add(archive, file_name_in_zip, source, ZIP_FL_ENC_UTF_8);
if (index < 0) {
fprintf(stderr, "Failed to add file to archive: %s\n", zip_strerror(archive));
zip_source_free(source);
zip_close(archive);
return EXIT_FAILURE;
}
// 4. 显式设置该文件的压缩方法为 Zstd
// ZIP_CM_ZSTD 是 libzip 中为 Zstandard 算法定义的常量
if (zip_set_file_compression(archive, index, ZIP_CM_ZSTD, 0) != 0) {
fprintf(stderr, "Failed to set compression method to Zstd: %s\n", zip_strerror(archive));
zip_close(archive);
return EXIT_FAILURE;
}
// 5. 关闭归档文件,将所有数据写入磁盘
if (zip_close(archive) != 0) {
fprintf(stderr, "Failed to close archive: %s\n", zip_strerror(archive));
return EXIT_FAILURE;
}
printf("Successfully created ZIP archive '%s' using Zstandard compression.\n", zip_filename);
return EXIT_SUCCESS;
}
压缩示例代码2:压缩内存中汉字字符串
// create_test_zip.c
#include <zip.h>
#include <stdio.h>
#include <string.h>
int main() {
const char *text = "这是一个使用Zstandard算法压缩的测试文件内容。";
struct zip *z = zip_open("test_zstd.zip", ZIP_CREATE | ZIP_TRUNCATE, NULL);
struct zip_source *s = zip_source_buffer(z, text, strlen(text), 0);
zip_int64_t index = zip_file_add(z, "test.txt", s, ZIP_FL_ENC_UTF_8);
zip_set_file_compression(z, index, ZIP_CM_ZSTD, 6); // 使用级别6
zip_close(z);
printf("创建测试文件完成: test_zstd.zip\n");
return 0;
}
解压缩示例代码
/**
* unzip_zstd.c - 解压支持 Zstandard 算法的 ZIP 文件
* 编译: gcc -o unzip_zstd unzip_zstd.c -lzip
* 使用: ./unzip_zstd <zipfile> [output_directory]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <zip.h>
#include <errno.h>
#ifdef _WIN32
#include <direct.h>
#define mkdir(path, mode) _mkdir(path)
#else
#include <unistd.h>
#endif
// 创建目录(包括父目录)
int create_directory(const char *path) {
char *path_copy = strdup(path);
char *p = path_copy;
int result = 0;
// 逐个创建目录
while (*p != '\0') {
if (*p == '/') {
*p = '\0'; // 临时截断字符串
if (strlen(path_copy) > 0) {
// 检查目录是否已存在
struct stat st;
if (stat(path_copy, &st) != 0) {
if (mkdir(path_copy, 0755) != 0 && errno != EEXIST) {
result = -1;
break;
}
}
}
*p = '/'; // 恢复斜杠
}
p++;
}
// 创建最终目录
if (result == 0) {
struct stat st;
if (stat(path_copy, &st) != 0) {
if (mkdir(path_copy, 0755) != 0 && errno != EEXIST) {
result = -1;
}
}
}
free(path_copy);
return result;
}
// 确保文件路径中的目录存在
int ensure_directory_exists(const char *filepath) {
char *path_copy = strdup(filepath);
char *last_slash = strrchr(path_copy, '/');
if (last_slash != NULL) {
*last_slash = '\0'; // 截断文件名部分,只保留目录路径
if (create_directory(path_copy) != 0) {
free(path_copy);
return -1;
}
}
free(path_copy);
return 0;
}
// 解压单个文件
int extract_file(struct zip *archive, zip_uint64_t index, const char *output_path) {
struct zip_file *zf;
FILE *outfile;
char buffer[8192];
zip_int64_t nbytes;
int err = 0;
// 打开 ZIP 中的文件
zf = zip_fopen_index(archive, index, 0);
if (zf == NULL) {
fprintf(stderr, "无法打开ZIP中的文件 #%lu: %s\n",
(unsigned long)index, zip_strerror(archive));
return -1;
}
// 确保输出目录存在
if (ensure_directory_exists(output_path) != 0) {
fprintf(stderr, "无法创建目录: %s\n", output_path);
zip_fclose(zf);
return -1;
}
// 创建输出文件
outfile = fopen(output_path, "wb");
if (outfile == NULL) {
fprintf(stderr, "无法创建文件 %s: %s\n", output_path, strerror(errno));
zip_fclose(zf);
return -1;
}
// 读取并写入文件数据
while ((nbytes = zip_fread(zf, buffer, sizeof(buffer))) > 0) {
if (fwrite(buffer, 1, nbytes, outfile) != (size_t)nbytes) {
fprintf(stderr, "写入文件失败 %s: %s\n", output_path, strerror(errno));
err = -1;
break;
}
}
if (nbytes < 0) {
fprintf(stderr, "读取ZIP文件失败: %s\n", zip_strerror(archive));
err = -1;
}
// 清理
fclose(outfile);
zip_fclose(zf);
return err;
}
// 获取压缩方法名称
const char *get_compression_name(zip_uint16_t method) {
switch (method) {
case ZIP_CM_STORE: return "Store (无压缩)";
case ZIP_CM_DEFLATE: return "Deflate";
case ZIP_CM_BZIP2: return "BZIP2";
case ZIP_CM_LZMA: return "LZMA";
case ZIP_CM_ZSTD: return "Zstandard (Zstd)";
case ZIP_CM_XZ: return "XZ";
default: return "Unknown";
}
}
// 主解压函数
int extract_zip(const char *zip_path, const char *output_dir) {
struct zip *archive;
int err = 0;
zip_int64_t num_entries, i;
// 打开 ZIP 文件
archive = zip_open(zip_path, 0, &err);
if (archive == NULL) {
zip_error_t error;
zip_error_init_with_code(&error, err);
fprintf(stderr, "无法打开ZIP文件 %s: %s\n", zip_path, zip_error_strerror(&error));
zip_error_fini(&error);
return -1;
}
// 获取文件数量
num_entries = zip_get_num_entries(archive, 0);
printf("ZIP文件中包含 %ld 个文件\n", (long)num_entries);
// 创建输出目录(如果不存在)
if (output_dir != NULL && strlen(output_dir) > 0) {
if (create_directory(output_dir) != 0) {
fprintf(stderr, "无法创建输出目录 %s\n", output_dir);
zip_close(archive);
return -1;
}
}
// 遍历并解压所有文件
for (i = 0; i < num_entries; i++) {
const char *name = zip_get_name(archive, i, 0);
if (name == NULL) {
fprintf(stderr, "无法获取文件 #%ld 的名称: %s\n",
(long)i, zip_strerror(archive));
continue;
}
// 跳过目录条目(以/结尾的)
if (name[strlen(name) - 1] == '/') {
printf("创建目录: %s\n", name);
char dir_path[1024];
if (output_dir != NULL) {
snprintf(dir_path, sizeof(dir_path), "%s/%s", output_dir, name);
} else {
snprintf(dir_path, sizeof(dir_path), "%s", name);
}
create_directory(dir_path);
continue;
}
// 获取文件信息
struct zip_stat st;
if (zip_stat_index(archive, i, 0, &st) == 0) {
printf("解压: %s [%s, 压缩后: %lu → 原始: %lu 字节]\n",
name,
get_compression_name(st.comp_method),
(unsigned long)st.comp_size,
(unsigned long)st.size);
}
// 构建输出路径
char output_path[1024];
if (output_dir != NULL) {
snprintf(output_path, sizeof(output_path), "%s/%s", output_dir, name);
} else {
snprintf(output_path, sizeof(output_path), "%s", name);
}
// 解压文件
if (extract_file(archive, i, output_path) == 0) {
printf("成功解压到: %s\n", output_path);
} else {
fprintf(stderr, "解压失败: %s\n", name);
}
}
// 关闭 ZIP 文件
if (zip_close(archive) != 0) {
fprintf(stderr, "关闭ZIP文件失败: %s\n", zip_strerror(archive));
err = -1;
}
return err;
}
void print_usage(const char *program_name) {
printf("用法: %s <zip文件> [输出目录]\n", program_name);
printf("示例:\n");
printf(" %s archive.zip # 解压到当前目录\n", program_name);
printf(" %s archive.zip ./output # 解压到output目录\n", program_name);
printf("\n支持包括 Zstandard (Zstd) 在内的多种压缩算法\n");
}
int main(int argc, char *argv[]) {
if (argc < 2) {
print_usage(argv[0]);
return 1;
}
const char *zip_file = argv[1];
const char *output_dir = (argc >= 3) ? argv[2] : ".";
printf("开始解压: %s\n", zip_file);
printf("输出到: %s\n", output_dir);
printf("----------------------------------------\n");
if (extract_zip(zip_file, output_dir) == 0) {
printf("----------------------------------------\n");
printf("解压完成!\n");
return 0;
} else {
printf("----------------------------------------\n");
printf("解压过程中出现错误\n");
return 1;
}
}
编译执行
首先编译zstd库并验证安装
tar xf zstd-1.5.7.tar.gz
cd zstd-1.5.7
make
make install
zstd --version
*** Zstandard CLI (64-bit) v1.5.7, by Yann Collet ***
然后编译支持zstd库的libzip,他说要加上-DENABLE_ZSTD=ON选项,其实libzip-1.11.4的CMakeLists.txt中默认设置option(ENABLE_ZSTD "Enable use of Zstandard" ON)了,不用手工再指定,编译完成后用ldd命令检查依赖libzstd.so即表示libzip支持zstd算法。
/shujv/par/libzip-1.11.4$ mkdir build2
/shujv/par/libzip-1.11.4$ cd build2
/shujv/par/libzip-1.11.4/build2$ cmake .. -DENABLE_ZSTD=ON
make
/shujv/par/libzip-1.11.4/build$ ldd lib/libzip.so
linux-vdso.so.1 => (0x0000007f81cd0000)
/usr/lib/libzfh.so (0x0000007f81c02000)
liblzma.so.5 => /usr/local/lib/liblzma.so.5 (0x0000007f81bcb000)
libzstd.so.1 => /usr/local/lib/libzstd.so.1 (0x0000007f81af6000)
再编译测试代码,可见默认系统unzip命令能识别但无法解压用zstd算法压缩的zip文件。
export C_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/shujv/par/libzip-1.11.4/build:/shujv/par/libzip-1.11.4/lib
export LIBRARY_PATH=$LIBRARY_PATH:/shujv/par/libzip-1.11.4/build/lib
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/shujv/par/libzip-1.11.4/build/lib
/shujv/par$ gcc -o zstd_zip zstd_zip.c -lzip
/shujv/par$ ./zstd_zip
Successfully created ZIP archive 'example_zstd.zip' using Zstandard compression.
/shujv/par$ unzip -l example_zstd.zip
存档文件: example_zstd.zip
Length Date Time Name
--------- ---------- ----- ----
90 2025-08-26 09:07 example.txt
--------- -------
90 1 file
/shujv/par$ unzip example_zstd.zip
存档文件: example_zstd.zip
skipping: example.txt unsupported compression method 93
再编译测试解压代码,可见可以解压zstd压缩包,输出解压后的原文件显示中文和英文正确
/shujv/par$ gcc -o unzipzstd unzipzstd.c -lzip
/shujv/par$ ./unzipzstd
用法: ./unzipzstd <zip文件> [输出目录]
示例:
./unzipzstd archive.zip # 解压到当前目录
./unzipzstd archive.zip ./output # 解压到output目录
支持包括 Zstandard (Zstd) 在内的多种压缩算法
/shujv/par$ ./unzipzstd example_zstd.zip
开始解压: example_zstd.zip
输出到: .
----------------------------------------
ZIP文件中包含 1 个文件
解压: example.txt [Zstandard (Zstd), 压缩后: 85 → 原始: 90 字节]
成功解压到: ./example.txt
----------------------------------------
解压完成!
/shujv/par$ gcc -o create_zstd_zip create_zstd_zip.c -lzip
/shujv/par$ ./create_zstd_zip
创建测试文件完成: test_zstd.zip
/shujv/par$ ./unzipzstd test_zstd.zip
开始解压: test_zstd.zip
输出到: .
----------------------------------------
ZIP文件中包含 1 个文件
解压: test.txt [Zstandard (Zstd), 压缩后: 72 → 原始: 63 字节]
成功解压到: ./test.txt
----------------------------------------
解压完成!
/shujv/par$ cat test.txt
这是一个使用Zstandard算法压缩的测试文件内容。/shujv/par$
/shujv/par$ cat example.txt
This is some text content that will be compressed using the powerful Zstandard algorithm!
/shujv/par$
2778

被折叠的 条评论
为什么被折叠?



