参考内容: 哈夫曼树和哈夫曼编码, 看完秒懂! 哈夫曼实现文件压缩解压缩(c语言) #include<bits/stdc++.h> using namespace std; #define BUFFER_SIZE 1024 #define CHAR_COUNT_LEN 256 // 哈夫曼树的结点结构 struct huffman_node { size_t times; // 本字符出现的次数 unsigned char val; // 字符的值 huffman_node *left; // 左孩子 huffman_node *right; // 右孩子 }; size_t char_count_info[CHAR_COUNT_LEN] = {}; // 统计出现字符频率 vector<huffman_node *> huffman_tree_nodes; // 动态数组用于存储哈夫曼树的结点 huffman_node *huffman_tree_root = NULL; // 哈夫曼树的根结点 map<unsigned char, string> huffman_codes_table; // 用于存储哈夫曼编码 // 打印哈夫曼编码表 void print_huffman_codes_table(map<unsigned char, string> huffman_codes_table) { printf("the huffman codes is:\n"); for(map<unsigned char, string>::iterator it = huffman_codes_table.begin(); it != huffman_codes_table.end(); it++) { printf("character '%c'(%d) : %s\n", (it->first >= 32 && it->first <= 126) ? it->first : '?', it->first, it->second.c_str()); } printf("\n"); } // 生成哈夫曼编码表(递归函数) void generate_huffman_codes_table(huffman_node* root, string code) { if(root == NULL) return; // 如果是叶子节点,保存编码 if(root->left == NULL && root->right == NULL) { huffman_codes_table[root->val] = code; } else { // 递归处理左右子树 generate_huffman_codes_table(root->left, code + "0"); generate_huffman_codes_table(root->right, code + "1"); } } // 排序辅助函数 bool cmp(huffman_node *a,huffman_node *b){ return a->times > b->times; } // 创建哈弗曼树 int generate_huffman_tree() { printf("begin generate huffman tree......\n"); while(huffman_tree_nodes.size() > 1) { // 按照字符出现频率进行降序排序 sort(huffman_tree_nodes.begin(), huffman_tree_nodes.end(), cmp); // 取出字符出现频率最小的两个 huffman_node *min1 = huffman_tree_nodes.back(); huffman_tree_nodes.pop_back(); huffman_node *min2 = huffman_tree_nodes.back(); huffman_tree_nodes.pop_back(); // 生成新的父亲结点,频率最小的结点作为左孩子 huffman_node *cur = (huffman_node *) malloc(sizeof(huffman_node)); cur->times = min1->times + min2->times; cur->left = min1; cur->right = min2; huffman_tree_nodes.push_back(cur); huffman_tree_root = cur; } printf("success generate huffman tree\n\n"); return 0; } // 逐一生成哈夫曼树结点,并存储于全局变量 tree_nodes 中 int generate_huffman_tree_nodes() { printf("begin generate huffman tree nodes......\n"); int nodes_count = 0; // 统计生成了多少个结点 for(int i = 0; i < CHAR_COUNT_LEN; i++) { // 出现次数为 0 的字符不需要生成结点 if(char_count_info[i] > 0) { nodes_count++; huffman_node *cur = (huffman_node *) malloc(sizeof(huffman_node)); cur->val = i; cur->times = char_count_info[i]; cur->left = NULL; cur->right = NULL; huffman_tree_nodes.push_back(cur); } } printf("success generate %d huffman tree nodes\n\n", nodes_count); return 0; } // 用于统计每个字符在文件出现的次数 // 统计结果存储在全局变量 char_count_info 中 int count_char(unsigned char buffer[], size_t len) { for(size_t i = 0; i < len; i++) { char_count_info[buffer[i]]++; } return 0; } // 读取指定文件内容,并统计字符出现的次数 int count_file_char(char *file_name) { FILE *file; // 打开文件句柄 file = fopen(file_name, "rb"); if (file == NULL) { printf("can't open file [%s], please check file name!\n", file_name); return -1; } unsigned char buffer[BUFFER_SIZE] = {}; // 读取文件时的缓冲区 size_t bytes_read = 0; // 每次读了多少个字节 size_t read_times_count = 1; // 总共读取了多少次了 // 循环读取直到文件结束,1 表示每次读取的数据块大小,单位为字节 while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) > 0) { printf("the %zu th read, read %zu byte! process......\n", read_times_count, bytes_read); count_char(buffer, bytes_read); read_times_count++; } fclose(file); printf("success read file: [%s] and count char\n\n", file_name); return 0; } int compress_file(char *input_file_name, char *output_file_name) { printf("begin cmpress [%s] -> [%s]\n", input_file_name, output_file_name); FILE *input = fopen(input_file_name, "rb"); FILE *output = fopen(output_file_name, "wb"); if(input == NULL || output == NULL) { printf("can't open file [%s] or [%s], please check file name!\n", input_file_name, output_file_name); return -1; } unsigned char buffer[BUFFER_SIZE]; // 文件读取缓冲区 size_t bytes_read; // 已经读取了多少字节 unsigned char current_byte = 0; // 当前字节 int bit_count = 0; // 已经处理了多少个二进制位 while((bytes_read = fread(buffer, 1, BUFFER_SIZE, input)) > 0) { for(size_t i = 0; i < bytes_read; i++) { string code = huffman_codes_table[buffer[i]]; int code_length = code.length(); for(int j = 0; j < code_length; j++) { char bit = code[j]; current_byte <<= 1; // 左移 1 位 if(bit == '1') { current_byte |= 1; } bit_count++; if(bit_count == 8) { // 凑齐了 8 位(1 个字节) fputc(current_byte, output); current_byte = 0; // 清零,继续下一个字节 bit_count = 0; } } } } // 处理最后一个不完整的字节 if(bit_count > 0) { current_byte <<= (8 - bit_count); // 左移补齐 0 fputc(current_byte, output); // 记录最后一个字节的有效位数 fputc(bit_count, output); } else { fputc(8, output); // 表示最后一个字节是完整的 } fclose(input); fclose(output); printf("success compress file: [%s] to [%s]\n\n", input_file_name, output_file_name); return 0; } // 获取指定文件大小 size_t get_file_size(char *file_name) { FILE *fp = fopen(file_name, "rb"); if (fp == NULL) { printf("can't open file [%s], please check file name!\n", file_name); return -1; } fseek(fp, 0, SEEK_END); size_t file_size = ftell(fp); fclose(fp); return file_size; } int uncompress_file(char *input_file_name, char *output_file_name) { printf("begin uncmpress [%s] -> [%s]\n", input_file_name, output_file_name); size_t input_file_size = get_file_size(input_file_name); FILE *input = fopen(input_file_name, "rb"); FILE *output = fopen(output_file_name, "wb"); if(input == NULL || output == NULL) { printf("can't open file [%s] or [%s], please check file name!\n", input_file_name, output_file_name); return -1; } // 3. 解压数据 huffman_node *current_node = huffman_tree_root; // 用于记录当前在哪个结点 unsigned char cur_byte; // 当前读取的字节 int byte_bits = 8; // 默认最后一个字节完整 long bytes_decoded = 0; // 已经解码的字节 while(bytes_decoded < input_file_size) { cur_byte = fgetc(input); bytes_decoded++; // 需要对最后一个字节处理,因为最后一个字节可能不满 if(bytes_decoded == input_file_size-1) { int bit_count = fgetc(input); // 最后一个字节有效位数 byte_bits = 8 - bit_count; } for(int i = 7; i >= 8 - byte_bits; i--) { int bit = (cur_byte >> i) & 1; if(bit) { // 1 往右走 current_node = current_node->right; } else { // 0 往左走 current_node = current_node->left; } // 如果到达叶子节点 if(!current_node->left && !current_node->right) { // 将当前结点的 val 写入文件 fputc(current_node->val, output); current_node = huffman_tree_root; // 回到根节点 } } } printf("success uncompress file: [%s] to [%s]\n\n", input_file_name, output_file_name); return 0; } // 释放哈夫曼树 void destroy_huffman_tree(huffman_node* root) { if (root == NULL) return; destroy_huffman_tree(root->left); destroy_huffman_tree(root->right); free(root); } // 哈夫曼函数 int huffman(char *file_name) { // 压缩后的文件名 char compress_file_name[strlen(file_name) + 8]; // 解压缩后的文件名 char uncompress_file_name[strlen(file_name) + 11]; strcpy(compress_file_name, file_name); strcat(compress_file_name, ".huffman"); strcpy(uncompress_file_name, "uncompress_"); strcat(uncompress_file_name, file_name); count_file_char(file_name); generate_huffman_tree_nodes(); generate_huffman_tree(); // 根据哈夫曼树生成哈夫曼编码表 huffman_node *root = huffman_tree_root; generate_huffman_codes_table(root, ""); print_huffman_codes_table(huffman_codes_table); compress_file(file_name, compress_file_name); uncompress_file(compress_file_name, uncompress_file_name); destroy_huffman_tree(huffman_tree_root); } int main(int argc, char** argv) { if(argc < 2) { printf("usage: %s <filename>\n", argv[0]); return -1; } else { printf("the file is [%s]\n", argv[1]); return huffman(argv[1]); } return 0; }
Read More ~