lzw压缩、解压缩 C语言算法实现

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

将源文件拷贝到当前目录下,打开源程序lzw.cbp,在main.cpp中更改源文件、压缩文件、解压文件的名称、路径:
#define S() (strcpy(sourfile, "sourfile.jpg")) //源文件名及路径
#define C() (strcpy(codefile, "codefile.lzw")) //压缩文件名及路径
#define D() (strcpy(destfile, "destfile.jpg")) //解压文件名及路径
下面是具体实现,总共四个文件:
//头文件模块:lzw.h
#ifndef LZW_H_INCLUDED
#define LZW_H_INCLUDED
#define N 90000
#define M 100
typedef unsigned int Uint;
/*
函数功能: 将sourfile文件用LZW压缩到codefile文件中
函数参数:待压缩文件/压缩目标文件
函数返回值:压缩成功(TRUE)/压缩失败(FALSE)*/
void LzwEncoding(char sourfile[], char codefile[]);
/*
函数功能: 将codefile文件用LZW解压到destfilefile文件中
函数参数:压缩目标文件/解压文件
函数返回值:解压成功(TRUE)/解压失败(FALSE)*/
void LzwDecoding(char codefile[], char destfile[]);
/*
函数功能:判断前缀数组与当前字符的结合是否在字典中存在索引
函数参数:当前字符(cbuff)
函数返回值:在字典中(TRUE)/不在字典中(F ALSE)*/
bool IsFindDictKey(Uint buff);
/*
函数功能:判断前缀数组与当前字符的结合是否在字典中存在索引
函数参数:前缀数组(preFix)/前缀数组的有效长度(preCount)/当前字符(cbuff)
函数返回值:在字典中(TRUE)/不在字典中(F ALSE)*/
bool IsDictValue(Uint preFix[], Uint preCount, Uint cbuff);
/*
函数功能:查找前缀数组在字典中的索引
函数参数:前缀数组(preFix)/前缀数组的有效长度(preCount)
函数返回值:前缀数组在字典中的索引*/
Uint FindDictKey(Uint preFix[], Uint preCount);
extern Uint dict[N][M]; //全局字典
extern char sourfile[20]; //全局带压缩文件
extern char codefile[20]; //全局压缩文件
extern char destfile[20]; //全局解压文件
#endif // LZW_H_INCLUDED
运行源程序将执行压缩机解压缩两个过程:
LzwEncoding(sourfile, codefile); //压缩文件sourfile到codefile LzwDecoding(codefile, destfile); //解压文件codefile到destfile
//主函数模块:main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lzw.h"
#define S() (strcpy(sourfile, "sourfile.pdf"))
#define C() (strcpy(codefile, "codefile.lzw"))
#define D() (strcpy(destfile, "destfile.pdf"))
using namespace std;
Uint dict[N][M]; //全局字典
int main(void)
{
char sourfile[20];
char codefile[20];
char destfile[20];
S();C();D();
LzwEncoding(sourfile, codefile);
LzwDecoding(codefile, destfile);
return 0;
}
//压缩模块:LzwEncoding.cpp
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "lzw.h"
using namespace std;
/*
函数功能:查找前缀数组在字典中的索引
函数参数:前缀数组(preFix)/前缀数组的有效长度(preCount) 函数返回值:前缀数组在字典中的索引*/
Uint DictKey(Uint preFix[], Uint preCount)
{
Uint i;
int flag = 0;
for (i = 0; i < N; i++)
{
Uint j = preCount;
if (dict[i][0] == preCount)
{
flag = 1;
while ( j-- )
{
if (dict[i][j+1] != preFix[j])
{
flag = 0;
break;
}
}
}
if ( flag ) break;
}
return i;
}
/*
函数功能:判断前缀数组与当前字符的结合是否在字典中存在索引
函数参数:前缀数组(preFix)/前缀数组的有效长度(preCount)/当前字符((int)sbuff[count]) 函数返回值:在字典中(TRUE)/不在字典中(F ALSE)*/
bool IsDictValue(Uint preFix[], Uint preCount, Uint cbuff)
{
int flag = 0;
for (Uint i = 0; i < N; i++)
{
if ((dict[i][0] == preCount+1) && (dict[i][preCount+1] == cbuff))
{
flag = 1;
for (Uint j = 0; j < preCount; j++)
{
if (dict[i][j+1] != preFix[j])
{
flag = 0;
break;
}
}
}
if ( flag ) break;
}
if ( flag )
return true;
else
return false;
}/*O(n*n)*/
/*
函数功能: 将sourfile文件用LZW压缩到codefile文件中
函数参数:待压缩文件/压缩目标文件
函数返回值:压缩成功(TRUE)/压缩失败(FALSE)*/
void LzwEncoding(char sourfile[], char codefile[])
{
FILE *sfp; //源文件句柄
FILE *cfp; //编码文件句柄
BYTE sbuff[N]; //源文件字节缓冲区
Uint cbuff[N]; //压缩文件字节缓冲区
Uint preCount; //前缀数组小标
Uint preFix[N]; //前缀数组
Uint dictCount = 255; //字典索引位置
Uint cCount = 0; //压缩字节缓冲区下标
Uint count = 0; //计数器
Uint code; //临时编码
if ((sfp = fopen(sourfile, "rb")) == NULL)
{
printf("Read source file error!");
exit( 0 );
}
if ((cfp = fopen(codefile, "wb")) == NULL)
{
printf("Write code file error!");
fclose(sfp);
exit( 0 );
}
//初始化字典
for (int i = 0; i < N; i++)
{
if (i < 256)
{
dict[i][0] = 1; //第i个索引处有一个字符
dict[i][1] = i; //第i个索引处的字符
}
else
{
dict[i][0] = 0; //第i个索引处没有字符
}
}
fseek(sfp, 0, SEEK_END);
long fl = ftell(sfp); //获取原文件的字节数
fseek(sfp, 0, SEEK_SET); //将文件指针移到文件开头处
fread(sbuff, sizeof(sbuff[0]), fl, sfp); //将文件一次性读到缓冲区
preCount = 0; //初始化前缀数组下标
while ( fl-- ) //读取源文件内容并进行编码
{
if (IsDictValue(preFix, preCount, (int)sbuff[count])) //当前字符串在字典中preFix[preCount++] = (int)sbuff[count]; //将当前字符复制给前缀数组
else
{ //当前字符串不在字典中dictCount++; //字典增长
//将前缀数组对应的索引写入编码文件
code = DictKey(preFix, preCount);
cbuff[cCount++] = code;
//将前缀数组的字符及当前字符添加到字典中
Uint icount = preCount; //记录前缀数组的有效长度
dict[dictCount][0] = icount+1; //将dictCount索引处的字符数记录到字典
while ( icount-- )
{
dict[dictCount][preCount-icount] = preFix[preCount-icount-1];
}
dict[dictCount][preCount-icount] = (int)sbuff[count];
preCount = 0;
preFix[preCount++] = (int)sbuff[count]; //将当前字符复制给前缀数组
}
count++;
}
code = DictKey(preFix, preCount);
cbuff[cCount++] = code;
fwrite(cbuff, sizeof(Uint), cCount, cfp); //将压缩文件整块写入文件
fclose(sfp);
fclose(cfp);
}/* O(n^2*m) */
//解压模块:LzwDecoding.cpp
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "lzw.h"
using namespace std;
/*
函数功能:判断buff是否为字典中的一个索引
函数参数:无符号整形数buff
函数返回值:是(TRUE)/否(FALSE)*/
bool IsDictKey(Uint buff)
{
if (dict[buff][0] == 0)
return false;
return true;
}
/*
函数功能: 将codefile文件用LZW解压到destfilefile文件中函数参数:压缩目标文件/解压文件
函数返回值:解压成功(TRUE)/解压失败(FALSE)*/
void LzwDecoding(char codefile[], char destfile[])
{
FILE *cfp; //编码文件句柄
FILE *dfp; //源文件句柄
Uint oldCount; //old数组下标
Uint oldCode[N] = {0}; //解码过的索引值??
Uint cbuff[N/4]; //压缩文件缓冲区
Uint cCount = 0; //压缩文件缓冲区下标
BYTE dbuff[N] = {0}; //解压文件缓冲区
Uint dCount = 0; //解压文件缓冲区下标
Uint dictCount = 255; //字典长度
Uint i, j; //循环变量
if ((cfp = fopen(codefile, "rb")) == NULL)
{
printf("Read coding file error!");
exit( 0 );
}
if ((dfp = fopen(destfile, "wb")) == NULL)
{
printf("Write decoding file error!");
fclose(cfp);
exit( 0 );
}
//初始化字典
for (i = 0; i < N; i++)
{
if (i < 256)
{
dict[i][0] = 1; //第i个索引处有一个字符
dict[i][1] = i; //第i个索引处的字符
}
else
{
dict[i][0] = 0; //第i个索引处没有字符
}
}
fseek(cfp, 0, SEEK_END);
long fl = ftell(cfp)/4; //获取原文件的编码数
fseek(cfp, 0, SEEK_SET); //将文件指针移到文件开头处
oldCount = 0; //初始化前缀数组下标、处理第一个编码fread(cbuff, sizeof(Uint), fl, cfp);//将压缩文件整块读入
dbuff[dCount++]=cbuff[cCount];
oldCode[oldCount++]=cbuff[cCount];
fl--;
while ( fl-- ) //读取源文件内容并进行编码
{
cCount++; //处理下一编码
dictCount++; //字典增长
if (IsDictKey(cbuff[cCount])) //字节在字典中
{
j = oldCount;
dict[dictCount][0] = oldCount+1;
while ( j-- ) //更新字典
dict[dictCount][oldCount-j] = oldCode[oldCount-j-1];
dict[dictCount][oldCount-j] = dict[cbuff[cCount]][1];
i = dict[cbuff[cCount]][0];
while ( i-- ) //将当前索引对应的字典值加入解压文件缓冲区dbuff[dCount++] = dict[cbuff[cCount]][dict[cbuff[cCount]][0]-i];
//更新前缀数组
oldCount = 0;
i = dict[cbuff[cCount]][0];
while ( i-- )
{
oldCode[oldCount]=dict[cbuff[cCount]][oldCount+1];
oldCount++;
}
}
else
{
i = oldCount;
while ( i-- ) //前缀数组中的字典值加入解压文件缓冲区
dbuff[dCount++]=oldCode[oldCount-i-1];
dbuff[dCount++]=oldCode[0];
dict[cbuff[cCount]][0] = oldCount+1;
j = oldCount;
while ( j-- ) //将前缀数组及前缀数组的第一个字符更新到字典
dict[dictCount][oldCount-j+1] = oldCode[oldCount-j];
dict[dictCount][oldCount-j]=oldCode[0];
oldCode[oldCount++]=oldCode[0];
}
}
fwrite(dbuff, sizeof(BYTE), dCount, dfp); //将解压文件缓冲区整块写入解压文件fclose(cfp);
fclose(dfp);
}
改进方案:用结构体代替数组实现全局字典字典!其实现很简单,留给读者自行解决!。

相关文档
最新文档