利用DeepSeek编写采用zstd算法的zip压缩工具

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$ 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值