LZW编解码算法实现

2024-04-17 17:32
文章标签 算法 实现 编解码 lzw

本文主要是介绍LZW编解码算法实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、LZW编码

1.编码原理

2.算法实现

3.结果分析

二、LZW解码

1.解码原理

2.算法实现

3.结果分析

三、不同文件格式的编码比较 


一、LZW编码

1.编码原理

LZW的编码思想是不断地从字符流中提取新的字符串,通俗地理解为新“词条”,然后用“代号”也就是码字表示这个“词条”。这样一来,对字符流的编码就变成了用码字去替换字符流,生成码字流,从而达到压缩数据的目的。 LZW 编码是围绕称为词典的转换表来完成的。 LZW 编码器通过管理这个词典完成输入与输出之间的转换。 LZW 编码器的输入是字符流,字符流可以是用8位ASCII字符组成的字符串,而输出是用n位(例如12位)表示的码字流。

LZW编码算法的步骤如下:

步骤1:将词典初始化为包含所有可能的单字符,当前前缀P初始化为空。
步骤2:当前字符C=字符流中的下一个字符。
步骤3:判断P+C是否在词典中:
如果是,则用C扩展P,即让P=P+C,返回到步骤2。
如果否,则输出与当前前缀P相对应的码字W;将P+C添加到词典中;令P=C,并返回到步骤2

2.算法实现

(1)初始化词典

// 初始化字典 
void InitDictionary( void){int i;for( i=0; i<256; i++){dictionary[i].suffix = i;dictionary[i].parent = -1;dictionary[i].firstchild = -1;dictionary[i].nextsibling = i+1;}dictionary[255].nextsibling = -1;next_code = 256;
}

(2)查找词典

 // 查找词典 
int InDictionary( int character, int string_code){int sibling;// 单个字符返回C if( 0>string_code) return character;// sinling是前缀的第一个子节点sibling = dictionary[string_code].firstchild;while( -1<sibling){  //没有找到 // 如果输入字符C是P的子节点的前缀,返回子节点的值if( character == dictionary[sibling].suffix) return sibling;// 否则指向子节点的兄弟节点,继续判断C是否是P的子节点sibling = dictionary[sibling].nextsibling;}return -1;
}

(3)更新词典

// 更新词典 
void AddToDictionary( int character, int string_code){int firstsibling, nextsibling;if( 0>string_code) return;dictionary[next_code].suffix = character;dictionary[next_code].parent = string_code;dictionary[next_code].nextsibling = -1;dictionary[next_code].firstchild = -1;firstsibling = dictionary[string_code].firstchild;if( -1<firstsibling){	//存在子结点 nextsibling = firstsibling;while( -1<dictionary[nextsibling].nextsibling ) nextsibling = dictionary[nextsibling].nextsibling;dictionary[nextsibling].nextsibling = next_code;}else{  // 之前没有子结点,设置为第一个 dictionary[string_code].firstchild = next_code;}next_code ++;
}

(4)LZW编码算法

// LZW编码 
void LZWEncode( FILE *fp, BITFILE *bf){int character;int string_code;int index;unsigned long file_length;fseek( fp, 0, SEEK_END);  // 指针移到文件尾部 file_length = ftell( fp); // 得到文件长度 fseek( fp, 0, SEEK_SET);  // 指针移到文件头部 BitsOutput( bf, file_length, 4*8);  // 输出文件长度32位 InitDictionary();  // 初始化词典 string_code = -1;while( EOF!=(character=fgetc( fp))){  // 指针移动,获取下一个字符,直至文件结束 index = InDictionary( character, string_code);  // 查找词典,判断P+C是否在字典中 if( 0<=index){ // 如果P+C在词典中,返回P+C string_code = index;  }else{	// 如果P+C不在词典中,输出P对应码字 output( bf, string_code);if( MAX_CODE > next_code){// 更新词典 AddToDictionary( character, string_code);}// 另P=C,继续移动指针 string_code = character;}}output( bf, string_code);
}

3.结果分析

使用txt文本文件进行编码调试,在Dev C++运行选项的参数中输入:

 txt文本文件如下:

编码后呈现的二进制文件如下,可以在右侧看到原文本内容:

 


二、LZW解码

1.解码原理

LZW解码算法开始时,译码词典和编码词典相同,包含所有可能的前缀根。具体解码算法如下:
步骤1:在开始译码时词典包含所有可能的前缀根。
步骤2:令CW:=码字流中的第一个码字。
步骤3:输出当前缀-符串string.CW到码字流。
步骤4:先前码字PW:=当前码字CW。
步骤5:当前码字CW:=码字流的下一个码字。
步骤6:判断当前缀-符串string.CW 是否在词典中。
(1)如果”是”,则把当前缀-符串string.CW输出到字符流。当前前缀P:=先前缀-符串string.PW。当前字符C:=当前前缀-符串string.CW的第一个字符。把缀-符串P+C添加到词典。
(2)如果”否”,则当前前缀P:=先前缀-符串string.PW。当前字符C:=当前缀-符串string.CW的第一个字符。输出缀-符串P+C到字符流,然后把它添加到词典中。
步骤7:判断码字流中是否还有码字要译。
(1)如果”是”,就返回步骤4。
(2)如果”否”,结束。

2.算法实现

(1)解码字符流

//  解码字符流,不断向上查找父母结点判断该码字对应字符串有几个字符 
int DecodeString( int start, int code){int count;count = start;while (0 <= code) {  // 上一个字符串不为空 d_stack[count] = dictionary[code].suffix;  // code后缀 code = dictionary[code].parent;  // 首字母给code count++;}return count;
}

(2)LZW解码

利用词典编码结构,不断向上查找母节点,判断该码字对应字符串有几个字符。同时将解码后的字符存入缓冲区,缓冲区内存放后面要用的前缀。

如果在解码中,码字>词典中的最后一个码字,就以为着该码字在词典中不存在。此时P=PW,把上一个字符串首位保存在character中(即将C存入缓冲区,C=CW),输出P+C并添加到词典。

// LZW解码 
void LZWDecode( BITFILE *bf, FILE *fp){int character=0;  // 字符C int new_code, last_code;  //上一个字符int phrase_length;  //字段长度unsigned long file_length;  //文件长度file_length = BitsInput(bf, 4 * 8);  //根据之前的编码得到文件长度if (-1 == file_length) file_length = 0;InitDictionary();  //初始化词典last_code = -1;while (0 < file_length) {//直到文件尾 new_code = input(bf);   //读入16位即第一个字符串if (new_code >= next_code) {//判断C是否在词典中,初始化后next_code=256d_stack[0] = character;  //不在词典中则把C存入d_stack[0]phrase_length = DecodeString(1, last_code);  //decodestring计算字符串长度,通过往上寻找母节点,同时解码}else {//如果字符串在词典中则直接用new_code计算phrase_length = DecodeString(0, new_code);}character = d_stack[phrase_length - 1];  //把第一个字符给Cwhile (0 < phrase_length) {phrase_length--;fputc(d_stack[phrase_length], fp);file_length--;  //倒序输出,同时控制整个循环要读的编码数减少}if (MAX_CODE > next_code) {//把新词条写进词典AddToDictionary(character, last_code);}last_code = new_code;  //P=C}
}

3.结果分析

将在LZW编码输出的二进制文件进行解码,输出文件text_out_decode.txt:


 三、不同文件格式的编码比较 

文件格式输入文件大小输出二进制文件大小压缩比
txt5KB4KB80%
png16KB25KB156.25%
bmp193KB217KB112.435%
pptx1870KB1417KB75.77%
xlsx19KB28KB147.37%
pdf42KB64KB152.38%
mp32050KB2544KB124.10%
wav6963KB8095KB116.26%
mp434986KB42538KB121.58%
avi1026KB1242KB121.05%

通过表格发现,LZW编码可以对文本文件txt、Powerpoint文件pptx起到压缩作用。LZW词典编码对重复度高的文件压缩率较高,txt和ppt由大量中文字符组成,重复度没那么高压缩率就低。而其他更多复杂的格式,本身压缩程度很高的文件如视音频文件格式,LZW压缩效果并不好。

遇到的一些问题:

  • 该实验需将文件放入工程中实现
  • 在Dev C++运行选项的参数中输入参数:E/D、输入文件名、输出文件名

参考博客:

​​​​​​LZW编码—图像压缩_糖糖糖-豆的博客-CSDN博客_lzw编码

LZW词典编码与解码_pzp49666的博客-CSDN博客_lzw编码与解码

这篇关于LZW编解码算法实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/912430

相关文章

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q