参考内容:
哈夫曼树和哈夫曼编码, 看完秒懂!
哈夫曼实现文件压缩解压缩(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 ~
标签:#
哈夫曼树