C语言Huffman压缩和解压

符号表结构体:struct node{ // 字符串形式存储的Huffman编码 char code[MAX_CODE_LENGTH]; // 这个字符在文件中出现的次数 long count; // 在生成Huffman树的时候是否已经被当作叶子节点 int checked; // 符号 char sym; // l...
摘要由CSDN通过智能技术生成

符号表结构体:

struct node
{
    // 字符串形式存储的Huffman编码
    char code[MAX_CODE_LENGTH];
    // 这个字符在文件中出现的次数
    long count;
    // 在生成Huffman树的时候是否已经被当作叶子节点
    int checked;
    // 符号
    char sym;
    // left和right只在生成Huffman树的时候有用
    struct node* next,*left,*right;
};

全局变量:

const int BIT_WIDTH_CHAR = 8;
const int END_SYM_FLAG = 200;// 符号的范围是-127~128
int gl_total_node_num = 0;// 符号表的总长度
int gl_source_file_length = 0;// 源文件的总长度

辅助函数:

// 在链表中查找指定的字符
// 参数:符号表
// 参数:字符
// 返回值:找到的节点的指针
struct node* content_char(struct node*,char);
// 在链表中查找指定编码
// 参数:符号表
// 参数:编码
// 返回值:找到的节点的指针
struct node* content_code(struct node* list,const char* ch);
// 根据字符创建一个新的节点
// 参数:字符
// 参数:计数
// 返回值:新节点指针
struct node* create_new_node(char ch,int count);
// 插入新节点到符号表的最前面
// 参数list:目标链表
// 参数new_node:新节点
// 返回值:插入后的链表
struct node* insert_node(struct node *list,struct node *new_node);
// 输出链表
// 参数list:目标链表
void print_list(struct node *list);
// 获取到最小的未被检查的count的节点,返回它的指针,并将其设置为检查过的
// 参数list_addr:链表头
// 返回值:第一个未检查的最少出现次数的结点指针
struct node* get_smallest_node(struct node *list_addr);

压缩:

第一步:建立符号表

在main函数中:获取文件的总长度,用于生成进度条

// 获取文件长度,以实现进度条
fseek(source_file,0,SEEK_END);
gl_source_file_length = ftell(source_file);
fseek(source_file,0,SEEK_SET);

扫描源文件,按字符读取,建立符号表,统计每个字符出现的次数

首先提示进度条,10个'.'为结束

将读取到的字符在已有的符号表中查找,如果已存在就将该节点的count+1,否则就创建新节点并插入到符号表中

统计符号表中总节点数

// 生成符号链表(含频率)
// 参数:源文件
// 参数:目标链表
// 返回值:符号链表
struct node* generate_count(FILE* f,struct node*list)
{
    printf("counting");
    int count=0;
    
    char ch;
    struct node *content_node;
    while(fread(&ch,sizeof(char),1,f)==1)
    {
        // 进度条
        count++;
        if(count%(gl_source_file_length/10+1)==0)
            printf(".");
        
        // 插入符号表
        content_node = content_char(list,ch);
        if(content_node)
            content_node->count++;
        else 
        {
            gl_total_node_num++;
            list=insert_node(list,create_new_node(ch,1));
        }
    }
    printf("\n");
    return list;
}

第二步:生成Huffman树

生成Huffman树

使用先前统计的符号表总数进行计数循环,因为生成树的所有非叶子节点共有叶子节点-1个

首先输出进度条

视初始时所有的符号表中的节点为只有一个节点的子树,获取两个最小的count的子树根节点指针,这是通过节点的checked属性实现的,如果使用了两个子树根节点生成新的子树

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
以下是C语言实现哈夫曼树压缩文件的步骤和代码示例: 1. 定义哈夫曼树节点结构体和哈夫曼编码表结构体 ```c typedef struct { unsigned int weight; // 字符权重 unsigned int parent, lchild, rchild; // 父节点、左子节点、右子节点 } HTNode, *HuffmanTree; typedef char** HuffmanCode; // 动态分配数组存储哈夫曼编码表 ``` 2. 定义哈夫曼树相关函数 ```c // 选择权值最小的两个节点 void select(HuffmanTree HT, int n, int* s1, int* s2); // 建立哈夫曼树 void createHuffmanTree(HuffmanTree* HT, int n); // 生成哈夫曼编码 void createHuffmanCode(HuffmanTree HT, HuffmanCode* HC, int n); // 压缩文件 void compressFile(char* inputFile, char* outputFile, HuffmanCode HC); // 解文件 void decompressFile(char* inputFile, char* outputFile, HuffmanTree HT, int fileLength); ``` 3. 实现哈夫曼树相关函数 ```c // 选择权值最小的两个节点 void select(HuffmanTree HT, int n, int* s1, int* s2) { int i; unsigned int min1 = UINT_MAX, min2 = UINT_MAX; // 初始化为最大值 for (i = 1; i <= n; i++) { if (HT[i].parent == 0) { // 只考虑未被选中的节点 if (HT[i].weight < min1) { min2 = min1; *s2 = *s1; min1 = HT[i].weight; *s1 = i; } else if (HT[i].weight < min2) { min2 = HT[i].weight; *s2 = i; } } } } // 建立哈夫曼树 void createHuffmanTree(HuffmanTree* HT, int n) { if (n <= 1) { return; } int m = 2 * n - 1; // 哈夫曼树总节点数 *HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode)); // 动态分配数组存储哈夫曼树 int i; for (i = 1; i <= n; i++) { // 初始化前n个节点 (*HT)[i].weight = 0; (*HT)[i].parent = 0; (*HT)[i].lchild = 0; (*HT)[i].rchild = 0; } for (i = n + 1; i <= m; i++) { // 初始化后m-n个节点 (*HT)[i].weight = 0; (*HT)[i].parent = 0; (*HT)[i].lchild = 0; (*HT)[i].rchild = 0; } for (i = 1; i <= n; i++) { // 输入前n个节点的权值 scanf("%d", &((*HT)[i].weight)); } int s1, s2; for (i = n + 1; i <= m; i++) { // 构造哈夫曼树 select(*HT, i - 1, &s1, &s2); (*HT)[s1].parent = i; (*HT)[s2].parent = i; (*HT)[i].lchild = s1; (*HT)[i].rchild = s2; (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight; } } // 生成哈夫曼编码 void createHuffmanCode(HuffmanTree HT, HuffmanCode* HC, int n) { *HC = (HuffmanCode)malloc((n + 1) * sizeof(char*)); // 动态分配数组存储哈夫曼编码表 char* code = (char*)malloc(n * sizeof(char)); // 分配临时存储编码的空间 code[n - 1] = '\0'; // 编码结束符 int i; for (i = 1; i <= n; i++) { // 逐个字符求哈夫曼编码 int start = n - 1; // 编码结束符位置 int c = i; // 从叶子节点开始向上回溯 int f = HT[i].parent; while (f != 0) { // 直到回溯到根节点 if (HT[f].lchild == c) { code[--start] = '0'; } else { code[--start] = '1'; } c = f; f = HT[f].parent; } (*HC)[i] = (char*)malloc((n - start) * sizeof(char)); // 分配存储编码的空间 strcpy((*HC)[i], &code[start]); // 复制编码 } free(code); // 释放临时存储编码的空间 } // 压缩文件 void compressFile(char* inputFile, char* outputFile, HuffmanCode HC) { FILE* in = fopen(inputFile, "rb"); // 以二进制方式打开输入文件 FILE* out = fopen(outputFile, "wb"); // 以二进制方式打开输出文件 unsigned char c; // 读入的字符 unsigned char buffer = 0; // 缓存区 int count = 0; // 缓存区中剩余的位数 while (fread(&c, sizeof(unsigned char), 1, in) == 1) { // 逐个字符读入 char* code = HC[c]; // 获取哈夫曼编码 while (*code != '\0') { // 逐位写入缓存区 if (*code == '1') { buffer = buffer | (1 << count); } count++; if (count == 8) { // 缓存区满了,写入输出文件 fwrite(&buffer, sizeof(unsigned char), 1, out); buffer = 0; count = 0; } code++; } } if (count > 0) { // 最后一个字节不足8位,补0写入输出文件 buffer = buffer << (8 - count); fwrite(&buffer, sizeof(unsigned char), 1, out); } fclose(in); // 关闭输入文件 fclose(out); // 关闭输出文件 } // 解文件 void decompressFile(char* inputFile, char* outputFile, HuffmanTree HT, int fileLength) { FILE* in = fopen(inputFile, "rb"); // 以二进制方式打开输入文件 FILE* out = fopen(outputFile, "wb"); // 以二进制方式打开输出文件 unsigned char c; // 读入的字符 int p = 2 * fileLength - 1; // 指向哈夫曼树根节点 while (fread(&c, sizeof(unsigned char), 1, in) == 1) { // 逐个字节读入 int i; for (i = 0; i < 8; i++) { // 逐位解码 if ((c & (1 << (7 - i))) == 0) { p = HT[p].lchild; } else { p = HT[p].rchild; } if (HT[p].lchild == 0 && HT[p].rchild == 0) { // 到达叶子节点,写入输出文件 fwrite(&(HT[p].weight), sizeof(unsigned char), 1, out); p = 2 * fileLength - 1; // 指向哈夫曼树根节点 } } } fclose(in); // 关闭输入文件 fclose(out); // 关闭输出文件 } ``` 4. 调用函数进行压缩文件 ```c int main() { HuffmanTree HT; HuffmanCode HC; int n = 256; // 字符集大小 createHuffmanTree(&HT, n); // 建立哈夫曼树 createHuffmanCode(HT, &HC, n); // 生成哈夫曼编码 compressFile("input.txt", "output.dat", HC); // 压缩文件 decompressFile("output.dat", "output.txt", HT, n); // 解文件 return 0; } ```
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值