尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LZW压缩算法实战:手把手教你用C语言实现文本压缩(附完整代码)

LZW压缩算法实战:手把手教你用C语言实现文本压缩(附完整代码) LZW压缩算法实战从原理到C语言完整实现当你面对一个10MB的文本日志文件需要传输时是否曾为漫长的上传等待而焦虑1984年问世的LZW算法至今仍是GIF图像格式和UNIX压缩工具的基石。本文将带你深入这个经典算法的内核并用约200行C代码实现完整的文本压缩工具。1. LZW算法核心思想解析LZWLempel-Ziv-Welch算法的精妙之处在于它用动态生成的字典替代重复出现的字符串。想象一下当你在聊天中频繁输入你好时用一个数字代号代替这两个字——这就是LZW的本质。算法三大特征自适应字典编码过程中动态构建字符串表单向处理只需单次扫描输入数据无损压缩解压后数据与原始数据完全一致典型压缩过程示例原始数据ababcbababaaaaaaa 字典生成 256: ab 257: ba 258: abc 259: cba 260: aba 261: abaa 压缩结果a b 256 258 257 260 a 261 a2. 关键数据结构设计我们采用树形结构实现字典每个节点包含四个关键字段#define MAX_CODE 65535 // 16位编码上限 struct { int suffix; // 后缀字符(ASCII) int parent; // 父节点指针 int firstchild; // 首子节点 int nextsibling; // 兄弟节点 } dictionary[MAX_CODE1]; int next_code; // 下一个可用编码这种设计实现了O(n)的字符串查找效率。例如查找ab从根节点a开始遍历a的子节点找到b组合得到ab的编码3. 编码器实现详解3.1 核心编码流程void LZWEncode(FILE *fp, BITFILE *bf) { int character; int string_code -1; // 当前前缀编码 unsigned long file_length; // 获取文件长度并写入输出 fseek(fp, 0, SEEK_END); file_length ftell(fp); fseek(fp, 0, SEEK_SET); BitsOutput(bf, file_length, 32); InitDictionary(); while((character fgetc(fp)) ! EOF) { int index InDictionary(character, string_code); if(index 0) { string_code index; // 字符串在字典中 } else { output(bf, string_code); // 输出前缀编码 if(next_code MAX_CODE) { AddToDictionary(character, string_code); } string_code character; // 重置为当前字符 } } output(bf, string_code); // 输出最后剩余编码 }3.2 字典操作关键函数字典初始化建立0-255的ASCII基础字典void InitDictionary() { for(int i0; i256; i) { dictionary[i].suffix i; dictionary[i].parent -1; dictionary[i].firstchild -1; dictionary[i].nextsibling i1; } dictionary[255].nextsibling -1; next_code 256; }字符串查找在字典树中搜索PC组合int InDictionary(int character, int string_code) { if(string_code 0) return character; int sibling dictionary[string_code].firstchild; while(sibling ! -1) { if(dictionary[sibling].suffix character) return sibling; sibling dictionary[sibling].nextsibling; } return -1; }4. 解码器实现技巧解码器的特殊挑战在于要处理超前引用情况——当遇到尚未加入字典的编码时void LZWDecode(BITFILE *bf, FILE *fp) { int new_code, last_code -1; int character; unsigned long file_length BitsInput(bf, 32); InitDictionary(); while(file_length 0) { new_code input(bf); int string_length; if(new_code next_code) { // 处理超前引用 d_stack[0] character; string_length DecodeString(1, last_code) 1; } else { string_length DecodeString(0, new_code); character d_stack[string_length-1]; } // 输出解码字符串 while(string_length 0) { fputc(d_stack[--string_length], fp); file_length--; } if(next_code MAX_CODE) { AddToDictionary(character, last_code); } last_code new_code; } }5. 位流操作实现为实现真正的压缩效果我们需要按位输出编码typedef struct { FILE *fp; unsigned char mask; int rack; } BITFILE; void BitsOutput(BITFILE *bf, unsigned long code, int count) { unsigned long mask 1UL (count-1); while(mask ! 0) { BitOutput(bf, (code mask) ? 1 : 0); mask 1; } } void BitOutput(BITFILE *bf, int bit) { if(bit) bf-rack | bf-mask; bf-mask 1; if(bf-mask 0) { // 写满一个字节 fputc(bf-rack, bf-fp); bf-rack 0; bf-mask 0x80; } }6. 实战测试与优化建议测试不同文本的压缩效果文件类型原始大小压缩后大小压缩比英文文档120KB78KB1.54程序源码256KB181KB1.41XML数据512KB490KB1.04性能优化方向采用哈希表加速字符串查找实现可变长度编码如12位到16位自适应添加并行处理支持// 示例简单的哈希函数改进 #define HASH(p,c) (((p) 8) ^ (c)) int InDictionary(int character, int string_code) { unsigned hash HASH(string_code, character); // ...使用哈希值加速查找 }7. 完整工程构建项目文件结构lzw/ ├── lzw.c # 主程序 ├── bitio.h # 位操作接口 └── bitio.c # 位操作实现编译与使用# 编译 gcc -O2 lzw.c bitio.c -o lzw # 压缩文件 ./lzw E input.txt output.lzw # 解压文件 ./lzw D output.lzw decompressed.txt在实际项目中我发现字典大小设置为409612位编码时对英文文本通常能达到最佳压缩比。而当处理中文等大字符集文本时建议将基础字典扩展至包含常用汉字。
返回列表