news 2026/5/5 12:38:41

哈夫曼压缩与关键字检索

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
哈夫曼压缩与关键字检索

https://github.com/nhaok169/huffman-compressor.git

一、设计思路

1、目标:

实现基于标准 ASCII (0–127) 的哈夫曼压缩、解压与在压缩文件中按原始字符串查找功能。

2、总体流程:

"encoder":读取文本文件,统计字符频率,构建哈夫曼树,生成每个字符的变长码,写出自定义二进制格式(文件头 "HUFF" + 原始字节数 + 字符表 + 压缩数据),并输出压缩比信息。

"decoder":读取二进制格式,重建哈夫曼解码树(从字符表),按位读取压缩数据,遍历树恢复原字节,写回解压后的文本文件。

"finder":在压缩文件中读取字符表并用其生成目标字符串对应的比特序列,然后在压缩比特流上做位级搜索(使用滑动窗口与类似 BoyerMoore 的部分跳跃优化),并通过遍历哈夫曼树统计并报告匹配出现的位置(以原字符序号计)。

3、项目文件(快速索引)

main.cpp:程序入口与参数解析。

encoder.h / encoder.cpp:压缩器声明与实现。

decoder.h / decoder.cpp:解压器声明与实现(含树结点创建/销毁、按位拆分函数)。

finder.h / finder.cpp:在压缩文件中查找原始字符串功能。

common.h:通用声明("split" 函数原型)。

哈夫曼压缩工具设计报告

4、文件格式设计

定义了专用的压缩文件格式,确保压缩数据的完整性和可识别性:

[4字节] 魔数: "HUFF" (0x48554646)

[4字节] 原始文件大小

[1字节] 字符种类数 N

[编码表] N个条目,每个包含:

[1字节] 字符

[1字节] 编码长度(bits)

[X字节] 编码数据((len+7)/8字节,高位对齐)

[压缩数据] 哈夫曼编码的bits流

二、代码说明

2.1 数据结构定义

2.1.1 哈夫曼树节点(encoder.h)

typedef struct htnode {

unsigned long weight; // 字符频次(权值)

int par; // 父节点索引

int lc, rc; // 左右孩子节点索引

} htnode;

2.1.2 编码信息结构(encoder.h)

typedef struct huffcode {

unsigned char src; // 源字符

unsigned char len; // 编码长度(位)

unsigned char bits[16]; // 编码数据(最大支持127位编码)

} huffcode;

2.1.3 解码树节点(decoder.h)

typedef struct denode {

unsigned char ch; // 叶子节点存储的字符

struct denode* left; // 左孩子指针(编码位0)

struct denode* right; // 右孩子指针(编码位1)

} denode, deptr;

2.1.4 查找模块编码节点(finder.h)

typedef struct {

unsigned char len; // 编码长度

unsigned char *code; // 编码位数组

}node, *codeptr;

2.2 核心函数说明

2.2.1 压缩模块(encoder.c)

主函数:encoder

int encoder(char *inputf, char *outputf);

功能:实现完整的哈夫曼编码压缩流程

流程:

  1. 统计字符频次
  2. 构建哈夫曼树
  3. 生成编码表
  4. 写入文件头
  5. 压缩数据写入
  6. 计算压缩率

辅助函数:tobyte

unsigned char tobyte(unsigned char* src, int len);

功能:将位数组转换为字节

参数:src-位数组指针,len-位数组长度

返回值:转换后的字节

说明函数:prompt2

void prompt2();

功能:显示压缩程序的使用说明和文件格式

2.2.2 解压模块(decoder.c)

主函数:decoder

int decoder(char *inputf, char *outputf);

功能:解压哈夫曼压缩文件

流程:

  1. 验证文件格式
  2. 读取编码表
  3. 重建哈夫曼解码树
  4. 解码数据
  5. 写入输出文件

树操作函数:

deptr createnode(); // 创建新节点

void destroy(deptr root); // 销毁解码树

void split(unsigned char ch, unsigned char *tem, int chlen);

// 字节拆分

说明函数:prompt

void prompt();

功能:显示解压程序的使用说明

2.2.3 查找模块(finder.c)

主函数:finder

int finder(char *inputf, char *seekword);

功能:在压缩文件中直接查找字符串

特点:

  • 无需完全解压文件
  • 使用滑动窗口技术
  • 应用Sunday算法优化匹配
  • 核心算法:
  1. 构建查找字符串的位模式
  2. 预计算跳转表(坏字符和好后缀规则)
  3. 滑动窗口匹配
  4. 实时解码定位

说明函数:prompt3

void prompt3();

功能:显示查找程序的使用说明

2.2.4 主程序(main.c)

主函数:main

int main(int argc, char *argv[]);

功能:命令行接口和功能分发

支持模式:

  • -e:压缩模式
  • -d:解压模式
  • -f:查找模式
  • 帮助函数:print_usage
  • void print_usage(const char *prog_name);
  • 功能:显示程序使用帮助

2.3 关键技术点

2.3.1 压缩优化

  • 小文件处理:特殊处理单字符文件
  • 内存管理:静态数组和动态分配结合
  • 边界检查:严格检查字符范围(0-127)

2.3.2 查找优化

  • 窗口技术:MAXR=100000定义滑动窗口大小
  • 跳表预计算:减少不必要的匹配尝试
  • 增量解码:仅解码必要部分

2.3.3 错误处理

  • 文件打开失败检测
  • 格式验证(魔数检查)
  • 内存分配失败处理
  • 无效字符范围检查

2.4 性能特点

  1. 时间复杂度:
    • 压缩:O(n log m),n为字符数,m为字符种类
    • 解压:O(n)
    • 查找:O(n + m),n为文件大小,m为模式长度
  2. 空间复杂度:
    • 压缩:O(1)额外空间(固定大小数组)
    • 查找:O(k)窗口空间,k为窗口大小
  3. 压缩率:
    • 对文本文件有较好的压缩效果
    • 小文件可能因编码表开销导致负压缩

三、分析

1. 查询错误的可能性与效率关系分析

1.1 错误可能性分析

当查询字符串的哈夫曼编码较短时,可能存在"伪匹配"问题。

风险场景示例:

假设:

- 字符'A'的编码:01(2位)

- 字符'B'的编码:10(2位)

- 查询字符串:"AB"的编码:0110

伪匹配风险:

原始文本是"XY",其中:

- 'X'的编码末尾位为:...01

- 'Y'的编码开头位为:...10

- 实际比特流:...01 10...

虽然编码边界在01和10之间,但在比特流层面,连续的0110会被误识别为"AB"。

错误概率计算:

- 假设字符集大小:m

- 平均编码长度:L bits

- 查询字符串长度:k 字符

- 查询编码总长:K bits

伪匹配概率 ≈ P ≈ (1/2)^(K-1) × (字符边界对齐概率)

1.2 效率关系

  • 查询字符串长度与效率的权衡:

查询长度

错误风险

匹配效率

内存开销

优化策略

过短(1-2字符)

高风险(伪匹配多)

高(候选多)

增加验证步骤

中等(3-10字符)

中等风险

中等

中等

平衡策略

较长(>10字符)

低风险

低(候选少)

预计算跳表

2. 压缩率优化分析

2.1 当前压缩率损失分析

主要损失点:编码表存储开销

// 每个字符存储:

// 1字节字符 + 1字节长度 + ceil(len/8)字节编码

// 对于短编码,存储开销可能大于压缩收益

小文件编码表占比过大

小文件压缩示例(50字符):

- 编码表:假设20个字符 × 平均3字节 = 60字节

- 数据压缩后:50字符 × 平均3位 = 约19字节

- 总大小:79字节 > 原始50字节(负压缩)

2.2 提高压缩率的具体方法

方法1:编码表也按位紧密排列(如你所提)

压缩率提升估计:

- 减少填充位:每个文件末尾最多节省7位

- 对小文件更显著:相对占比更高

方法2:动态块压缩

思路:

- 将大文件分成块(如64KB)

- 每块独立统计频率、生成编码

- 编码表只需存储差异部分

优点:

- 适应局部统计特性

- 减少编码表存储

四、使用限制

  1. 字符范围:仅支持标准ASCII字符(0-127)
  2. 文件大小:支持最大4GB文件
  3. 编码长度:单个字符编码最长127位
  4. 内存要求:查找功能需要较大的窗口内存

五、扩展性

  1. 支持UTF-8编码
  2. 添加多线程压缩/解压
  3. 支持目录批量处理
  4. 添加进度显示
  5. 支持更多压缩参数调整
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 8:32:29

虚拟机上由于网络问题无法正常git clone

命令: git clone https://github.com/IFL-CAMP/easy_handeye.git #​(https://github.com/IFL-CAMP/easy_handeye.git 是官方的easy_handeye,手眼标定包,支持ROS Melodic)。​ 报错“gnutls_handshake() failed: Err…

作者头像 李华
网站建设 2026/4/23 19:13:47

2026高职商务数据分析师必考证书指南

商务数据分析师是当前热门职业之一,随着数据驱动决策的需求增长,相关证书的含金量也在不断提升。以下是2026年高职商务数据分析师必考证书的详细分析,涵盖证书类型、考试内容、适用人群及推荐理由。CDA数据分析师认证CDA(Certifie…

作者头像 李华
网站建设 2026/4/21 16:03:57

对象是啥,类的构造器,this及他们的使用场景

对象到底是啥ps:对象就是一种特殊的数据结构,类是一个模板,对象是用类new出来的,有了类就可以创建出对象。构造器的使用是为了方便给对象属性赋值ps:变量存在栈里,变量指向对象,对象存在堆里,对象指向类&am…

作者头像 李华
网站建设 2026/5/5 0:27:31

30、脚本杂谈:transpose、m1 宏处理器与 sed 快速参考

脚本杂谈:transpose、m1 宏处理器与 sed 快速参考 1. transpose 脚本 transpose 是一个简单却有趣的脚本,以下是它的测试示例: $ transpose test 1 5 9 2 6 10 3 7 11 4 8 12其程序逻辑是创建一个名为 row 的数组,将每个字段追加到数组元素中,最后通过 END 过程输…

作者头像 李华
网站建设 2026/4/29 15:23:53

Kotaemon能否识别服装搭配?时尚产业智能顾问

Kotaemon能否识别服装搭配?时尚产业智能顾问 在一家高端女装品牌的线上客服后台,一位用户输入:“我身高160,梨形身材,下周要参加婚礼,想要一条显瘦又不失优雅的连衣裙。”传统推荐系统可能只会返回“高腰A字…

作者头像 李华
网站建设 2026/5/1 10:48:33

9、数据库导入Web应用的全流程指南

数据库导入Web应用的全流程指南 1. 建立新关系 在运行查询之后,你可以基于新的主键和外键在两个表之间创建新的关系。具体操作如下: - 在关系图中通过拖放操作,基于每个表中的CustID字段创建一个新的关系,并强制实施引用完整性(可参考相关图示)。 - 无需创建新的查找…

作者头像 李华