简化版文件封包解包程序

三个部分

文件头

索引表

文件数据

索引表的每个offset记录的是每个诗句相对于数据区起始位置的偏移量

// 简化版文件封包解包程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>

#define PACK_FILE "archive.dat"
#define MAX_PATH 256

// 文件头结构
struct FileHeader
{
    int file_count;  // 文件数量
    long data_start; // 数据区起始位置
};

// 文件索引结构
struct FileIndex
{
    char name[256]; // 文件名
    long offset;    // 每个文件相对于数据区起始位置的偏移
    long size;      // 文件大小
};

// 获取文件大小
long get_file_size(FILE *fp)
{
    long size;
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    return size;
}

// 打包函数
void pack_folder(const char *folder)
{
    printf("正在打包文件夹: %s\n", folder);

    // 打开文件夹
    DIR *dir = opendir(folder);
    if (!dir)
    {
        printf("无法打开文件夹\n");
        return;
    }

    // 收集文件信息
    struct dirent *entry;
    struct FileIndex *indexes = NULL;
    int file_count = 0;
    long current_offset = 0;

    // 第一遍:统计文件
    while ((entry = readdir(dir)) != NULL)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        char fullpath[512];
        snprintf(fullpath, sizeof(fullpath), "%s/%s", folder, entry->d_name);

        struct stat st;
        if (stat(fullpath, &st) == 0 && S_ISREG(st.st_mode))
        {
            file_count++;
        }
    }

    if (file_count == 0)
    {
        printf("文件夹为空\n");
        closedir(dir);
        return;
    }

    // 分配索引数组
    indexes = malloc(file_count * sizeof(struct FileIndex));
    if (!indexes)
    {
        printf("内存分配失败\n");
        closedir(dir);
        return;
    }

    // 第二遍:收集文件信息
    rewinddir(dir);
    int idx = 0;
    while ((entry = readdir(dir)) != NULL)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        char fullpath[512];
        snprintf(fullpath, sizeof(fullpath), "%s/%s", folder, entry->d_name);

        struct stat st;
        if (stat(fullpath, &st) == 0 && S_ISREG(st.st_mode))
        {
            // 获取文件大小
            FILE *fp = fopen(fullpath, "rb");
            if (fp)
            {
                strncpy(indexes[idx].name, entry->d_name, 255);
                indexes[idx].name[255] = '\0';
                indexes[idx].offset = current_offset;
                indexes[idx].size = get_file_size(fp);
                current_offset += indexes[idx].size;
                fclose(fp);
                idx++;
            }
        }
    }
    closedir(dir);

    // 创建封包文件
    FILE *pack = fopen(PACK_FILE, "wb");
    if (!pack)
    {
        printf("无法创建封包文件\n");
        free(indexes);
        return;
    }

    // 写入文件头
    struct FileHeader header;
    header.file_count = file_count;
    header.data_start = sizeof(struct FileHeader) + file_count * sizeof(struct FileIndex);
    fwrite(&header, sizeof(struct FileHeader), 1, pack);

    // 写入索引表
    fwrite(indexes, sizeof(struct FileIndex), file_count, pack);

    // 写入文件数据
    for (int i = 0; i < file_count; i++)
    {
        char fullpath[512];
        snprintf(fullpath, sizeof(fullpath), "%s/%s", folder, indexes[i].name);

        FILE *src = fopen(fullpath, "rb");
        if (src)
        {
            char buffer[4096];
            size_t read;
            while ((read = fread(buffer, 1, sizeof(buffer), src)) > 0)
            {
                fwrite(buffer, 1, read, pack);
            }
            fclose(src);
            printf("打包: %s (%ld 字节)\n", indexes[i].name, indexes[i].size);
        }
    }

    fclose(pack);
    free(indexes);
    printf("打包完成! 共 %d 个文件\n", file_count);
}

// 解包函数
void unpack_files(const char *packfile)
{
    printf("正在解包: %s\n", packfile);

    FILE *pack = fopen(packfile, "rb");
    if (!pack)
    {
        printf("无法打开封包文件\n");
        return;
    }

    // 读取文件头
    struct FileHeader header;
    if (fread(&header, sizeof(struct FileHeader), 1, pack) != 1)
    {
        printf("读取文件头失败\n");
        fclose(pack);
        return;
    }

    printf("文件数量: %d\n", header.file_count);

    // 读取索引表
    struct FileIndex *indexes = malloc(header.file_count * sizeof(struct FileIndex));
    if (!indexes)
    {
        printf("内存分配失败\n");
        fclose(pack);
        return;
    }

    if (fread(indexes, sizeof(struct FileIndex), header.file_count, pack) != header.file_count)
    {
        printf("读取索引表失败\n");
        free(indexes);
        fclose(pack);
        return;
    }

    // 定位到数据区
    fseek(pack, header.data_start, SEEK_SET);

    // 解包所有文件
    for (int i = 0; i < header.file_count; i++)
    {
        printf("解包: %s (%ld 字节)\n", indexes[i].name, indexes[i].size);

        FILE *out = fopen(indexes[i].name, "wb");
        if (!out)
        {
            printf("无法创建文件: %s\n", indexes[i].name);
            continue;
        }

        // 读取并写入文件数据
        long remaining = indexes[i].size;
        char buffer[4096];
        // 这段代码是用于分块读取文件数据的逻辑。
        /*
        remaining 变量
表示文件还剩下多少字节需要读取
初始值是文件总大小
每次读取后减少已读取的字节数*/
/*
这个循环就像用勺子舀水:
remaining = 锅里还剩多少水
buffer = 勺子的大小(固定4KB)
to_read = 这次要舀多少(如果水少就少舀点)
每次舀一勺,直到舀完为止*/
        while (remaining > 0)
        {
            size_t to_read = sizeof(buffer);
            if (remaining < (long)to_read)
            {
                to_read = remaining;
            }

            size_t read = fread(buffer, 1, to_read, pack);
            if (read == 0)
                break;

            fwrite(buffer, 1, read, out);
            remaining -= read;
        }

        fclose(out);
    }

    free(indexes);
    fclose(pack);
    printf("解包完成!\n");
}

// 主函数
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("使用方法:\n");
        printf("  打包: %s -pack 文件夹名\n", argv[0]);
        printf("  解包: %s -unpack archive.dat\n", argv[0]);
        return 1;
    }

    if (strcmp(argv[1], "-pack") == 0)
    {
        pack_folder(argv[2]);
    }
    else if (strcmp(argv[1], "-unpack") == 0)
    {
        unpack_files(argv[2]);
    }
    else
    {
        printf("参数错误\n");
        return 1;
    }

    return 0;
}

 

posted on 2025-12-27 21:24  小沙盒工作室  阅读(0)  评论(0)    收藏  举报