个人博客地址:https://travis1024.github.io/
RLE压缩解压算法
1. 问题描述
编写一个程序,可以在命令行输入参数,完成指定文件的压缩解压,命令行参数如下:rle file1 –c(-d) file2
第一个参数为可执行程序名称,第二个参数为原始文件名,第三个参数为压缩或解压缩选项,第四个参数为新文件名。
2. 问题分析
<1>需查询RLE算法的具体实现原理,掌握如何用C++实现RLE算法;
<2> 需解决二进制分块读入文件并且能够进行压缩和解压缩的问题;
3. 处理方法设计(算法步骤)
-
RLE算法的基本思路是把数据按照线性序列分成两种情况:一种是连续的重复数据块,另一种是连续的不重复数据块。对于第一种情况,对连续的重复数据块进行压缩,压缩方法就是用一个表示块数的属性加上一个数据块代表原来连续的若干块数据。对于第二种情况,RLE算法有两种处理方法,一种处理方法是用和第一种情况一样的方法处理连续的不重复数据块,仅仅是表示块数的属性总是1;另一种处理方法是不对数据进行任何处理,直接将原始数据作为压缩后的数据。
-
RLE算法的原理就是用一个表示块数的属性加上一个数据块代表原来连续的若干块数据,从而达到节省存储空间的目的。一般RLE算法都选择数据块的长度为 1 字节,表示块数的诚性也用1字节表示,对于颜色数小于 256 色的图像文件或文本文件,块长度选择 1 字节是比较合适的
-
**编码算法实现:**编码算法从数据的起始位置开始向后搜索,如果发现后面是重复数据且重复次数超过 2,则设置连续重复数据的标志并继续向后查找,直到找到第一个与之不相同的数据为止,将这个位置记为下次搜索的起始位置,根据位置差计算重复次数,最后长度属性字节以及一个字节的原始重复数据一起写入压缩数据;如果后面数据不是连续重复数据,则继续向后搜索查找连续重复数据,直到发现连续重复的数据且重复次数大于 2 为止,然后设置不重复数据标志,将新位置记为下次搜索的起始位置,最后将长度属性字节写入压缩数据并将原始数据逐字节复制到压缩数据。然后从上一步标记的新的搜索起始位开始,一直重复上面的过程,直到原始数据结束。
-
RLE_encoding()函数l 通过调用IsRepetition()函数判断从 src 开始的数据是否是连续重复数据。
如果是连续重复数据,则调用 GetRepetitionLength() 函数计算出连续重复数据的长度,将长度属性字节的最高位罝 1 并向输出缓冲区写入一个字节的重复数据。
如果不是连续重复数据,则调用 GetNotRepetitionLength() 函数计算连续非重复数据的长度,将长度属性字节的极高位罝 0 并向输出缓冲区复制连续的多个非重复数据。
根据算法要求,只有数裾重复出现两次以上才算作连续重复数据,因此 IsRepetition () 函数检査连续的3字节是否是相同的数据,如果是则判定为出现连续重复数据。之所以要求至少要 3 字节的重复数据才判定为连续重复数据,是为了尽量优化对短重复数据间隔出现时的压缩效率。
-
**解压缩算法:**两种情况下的压缩数据首部都是 1 字节的长度属性标识,我们需要根据这个标识判断如何处理。首先从压缩数据中取出 1 字节的长度属性标识,然后判断是连续重复数据的标识还是连续非重复数据的标识:如果是连续重复数据,则将标识字节后面的数据重复复制 n 份写入输出缓冲区;如果是连续重复数据,则将标识字节后面的数据重复复制 n 份写入输出缓冲区;
-
RLE_decoding()函数是解压缩算法的具体实现,每组数据的第一字节是长度标识字节,其最高位是标识位,低 7 位是数据长度属性,根据标识位分别进行处理即可。
4.测试数据设计
设计了两种测试文件,一种为txt文件,一种为bmp文件;
-
Text.txt文件:(命令行如下)
Fourth Text.txt -c newText.txt 生成压缩后的文件;
Fourth newText.txt -d oldText.txt 生成解压后的文件; -
Travis.bmp文件:(命令行如下)
Fourth travis.bmp -c newTravis.bmp 生成压缩后的文件;
Fourth newTravis.bmp -d oldTravis.bmp 生成解压后的文件;
5.测试结果分析
6.程序结构
7.源代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int IsRepetition(unsigned char *src,int srcLeft){ //判断是否含有数量>3的重复数据
if(srcLeft<3) return 0;
if((src[0]==src[1])&&(src[1]==src[2])) return 1;
return 0;
}
int GetRepetitionLength(unsigned char *src,int srcLeft){ //计算重复数据长度
int data=src[0],length=1;
while(length<srcLeft&&length<0x7f&&src[length]==data)
length++;
return length;
}
int GetNotRepetitionLength(unsigned char *src,int srcLeft){ //计算非重复数据长度
if(srcLeft<3) return srcLeft; //设定不重复长度
int length=2,a=src[0],b=src[1]; //a,b表示前后两个字符,不断向后移动
while(length<srcLeft&&length<0x7f&&((a!=b)||(b!=src[length]))){
a=b;
b=src[length];
length++;
}
return length;
}
int RLE_encoding(unsigned char *instring,int inSize,unsigned char *outstring,int onuBufSize){
unsigned char *src=instring; //RLE编码算法
int i,encSize=0,srcLeft=inSize,count;
while(srcLeft>0){
if(IsRepetition(src,srcLeft)){
if((encSize+2)>onuBufSize) return -1;
count=GetRepetitionLength(src,srcLeft);
outstring[encSize++]=count|0x80; //重复数据数据量
outstring[encSize++]=*src; //写字符
src+=count;
srcLeft-=count;
}else{
count=GetNotRepetitionLength(src,srcLeft);
if((encSize+count+1)>onuBufSize) return -1;
outstring[encSize++]=count;
for(i=0;i<count;i++)
outstring[encSize++]=*src++;
srcLeft-=count;
}
}
return encSize;
}
int RLE_decoding(unsigned char *instring,int inSize,unsigned char *outstring,int onuBufSize){
unsigned char *src=instring; //RLE解码算法
int i,decSize=0,count;
while(src<(instring+inSize)){
unsigned char sign=*src++;
count=sign & 0x7F;
if((decSize+count)>onuBufSize) return -1;
if((sign&0x80)==0x80){ //重复字符
for(i=0;i<count;i++)
outstring[decSize++]=*src;
src++;
}else{
for(i=0;i<count;i++)
outstring[decSize++]=*src++;
}
}
return decSize;
}
int main(int argc,char**argv){
int length,tmp;
FILE *Input=fopen(argv[1], "rb");
FILE *Output=fopen(argv[3], "wb");
if (Input==NULL||Output==NULL){
cout<<"File not found!"<<endl;
}
unsigned char*instring=(unsigned char*)malloc((sizeof(unsigned char))*1024*1024*1024);
unsigned char*outstring=(unsigned char*)malloc((sizeof(unsigned char))*1024*1024*1024);
if(strcmp(argv[2],"-c")==0){
while ((length=fread(instring, sizeof(unsigned char),1024,Input))!= 0){
int tmp=RLE_encoding(instring,length,outstring,1024*1024*1024);
fwrite(outstring, sizeof(unsigned char),tmp,Output);
}
}else if(strcmp(argv[2],"-d")==0){
while((length=fread(instring, sizeof(unsigned char),1024*1024*1024,Input))!=0){
int tmp=RLE_decoding(instring,length,outstring,1024*1024*1024);
if(tmp==-1){
return -2;
}
fwrite(outstring, sizeof(unsigned char),tmp,Output);
}
}
fclose(Input);
fclose(Output);
cout<<"Successful!"<<endl;
return 0;
}