【C】7z源代码的使用——LZMA压缩解压算法
7z是目前流行的高压缩比压缩软件。它的压缩比甚至超过了WinRAR的最高压缩+固实压缩包(虽然从速度上WinRAR比7z快很多)。7z使用了LZMA算法(这个LZMA不要把它联想成“楼主默哀”的拼音首字母)。而且7z是开源的。因此我们可以引用7z的压缩代码进行压缩解压。
首先我们需要下载7z的源代码。
LZMA的SDK官网:
http://www.7-zip.org/sdk.html
LZMA SDK下载地址:
http://ncu.dl.sourceforge.net/project/sevenzip/LZMA%20SDK/lzma920.tar.bz2
笔者下载的是9.20稳定版的源码。
从源代码中我们会看到很多文件。其中,LZMA的算法部分是以下这些文件:(在C文件夹内)
Alloc.c
Alloc.h
Bra.c
Bra.h
Bra86.c
BraIA64.c
LzFind.c
LzFind.h
LzFindMt.c
LzFindMt.h
LzHash.h
Lzma2Dec.c
Lzma2Dec.h
Lzma2Enc.c
Lzma2Enc.h
Lzma86.h
Lzma86Dec.c
Lzma86Enc.c
LzmaDec.c
LzmaDec.h
LzmaEnc.c
LzmaEnc.h
LzmaLib.c
LzmaLib.h
MtCoder.c
MtCoder.h
Threads.c
Threads.h
Types.h
好像很多的样子。把它们复制出来吧。
其中对于使用LZMA的我们来说,只需要关心LzmaLib.h即可。这个头文件也很简单,导出了两个函数,LzmaCompress和LzmaUncompress。
LzmaCompress用于压缩,而LzmaUncompress用于解压缩。
它们的原型如下:MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen,
unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */
int level, /* 0 <= level <= 9, default = 5 */
unsigned dictSize,/* default = (1 << 24) */
int lc, /* 0 <= lc <= 8, default = 3*/
int lp, /* 0 <= lp <= 4, default = 0*/
int pb, /* 0 <= pb <= 4, default = 2*/
int fb, /* 5 <= fb <= 273, default = 32 */
int numThreads /* 1 or 2, default = 2 */
);
MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen,
const unsigned char *props, size_t propsSize);其中有个概念是“Props”(全写应该是Properties,属性的意思)
LzmaCompress在压缩的时候会产生一个5字节的包,存储“属性”。在解压缩的时候,LzmaUncompress就需要你提供这个包,否则无法完成解压缩。
参数lc、lp、pb、fb、numThreads可以直接给-1,这样LzmaCompress就会自动填写默认值。dictSize给0也表示使用默认值。
因为属性包的大小目前被定死为5字节,因此*outPropsSize的值必须为5。outProps这个内存块也要有至少5字节的容量。
这里笔者我写了一些代码来应用LZMA的算法完成一些非常简单的压缩和解压缩的试验。
我建立了一个VC6的工程(VS2012在我这台笔记本上启动太慢,而且它会产生一些奇怪的东西总之就是麻烦。),新建了一个Entry.c作为程序入口点。
试验的方法是编写一个专门用来压缩、解压单个文件的程序,来试试效果。
代码如下。#include<stdio.h>
#include<malloc.h>
#include"lzmalib.h"
void Usage()
{
fputs(
"Usage:\n"
"LZMAComp <command> <input> <output>\n"
"Command:\n"
"-c: Compress a single file <input> into <output>.\n"
"-d: Decompress a single file <input> into <output>.\n",stderr);
}
int CompressFile(FILE*fpOut,FILE*fpIn,unsigned long InSize);
int DecompressFile(FILE*fpOut,FILE*fpIn);
int main(int argc,char**argv)
{
if(argc<4)
{
Usage();
return 1;
}
if(!stricmp(argv,"-c"))//压缩一个文件
{
FILE*fp=fopen(argv,"rb");
FILE*fpout=fopen(argv,"wb");
int iRet;
unsigned long fLen;
if(!fp)
{
fprintf(stderr,"Unable to open %s\n",argv);
return 2;
}
if(!fpout)
{
fprintf(stderr,"Unable to write %s\n",argv);
return 2;
}
fseek(fp,0,SEEK_END);
fLen=ftell(fp);
fseek(fp,0,SEEK_SET);
printf("Input file size=%u\n",fLen);
iRet=CompressFile(fpout,fp,fLen);
if(iRet)
fprintf(stderr,"Error:%d\n",iRet);
fclose(fpout);
fclose(fp);
if(iRet)
unlink(argv);
return iRet;
}
if(!stricmp(argv,"-d"))//解压一个文件
{
FILE*fp=fopen(argv,"rb");
FILE*fpout=fopen(argv,"wb");
int iRet;
if(!fp)
{
fprintf(stderr,"Unable to open %s\n",argv);
return 2;
}
if(!fpout)
{
fprintf(stderr,"Unable to write %s\n",argv);
return 2;
}
iRet=DecompressFile(fpout,fp);
if(iRet)
fprintf(stderr,"Error:%d\n",iRet);
fclose(fpout);
fclose(fp);
if(iRet)
unlink(argv);
return iRet;
}
Usage();
return 1;
}
int CompressFile(FILE*fpOut,FILE*fpIn,unsigned long InSize)
{
void*pInBuffer;//输入缓冲区
void*pOutBuffer;//输出缓冲区
unsigned long OutSize;//输出缓冲区大小
unsigned char Props;//属性
size_t PropsSize=LZMA_PROPS_SIZE;//属性大小
pInBuffer=malloc(InSize);//缓冲区分配内存
pOutBuffer=malloc(OutSize=InSize);//输出缓冲区分配和输入缓冲区一样大的内存
if(!pInBuffer||!pOutBuffer)
{
free(pInBuffer);
free(pOutBuffer);
return 2;
}
fread(pInBuffer,1,InSize,fpIn);//读取文件
switch(LzmaCompress(//开始压缩
pOutBuffer,&OutSize,//输出缓冲区,大小
pInBuffer,InSize,//输入缓冲区,大小
Props,&PropsSize,//属性,属性大小
9,0,-1,-1,-1,-1,-1))//压缩比最大。其余全部取默认
{
case SZ_OK://成功完成
fwrite(&InSize,1,sizeof(InSize),fpOut);//写入原数据大小
fwrite(&OutSize,1,sizeof(OutSize),fpOut);//写入解压后的数据大小
fwrite(Props,1,PropsSize,fpOut);//写入属性
fwrite(pOutBuffer,1,OutSize,fpOut);//写入缓冲区
free(pInBuffer);//释放内存
free(pOutBuffer);
return 0;
case SZ_ERROR_PARAM://参数错误
free(pInBuffer);
free(pOutBuffer);
return 1;
default:
case SZ_ERROR_MEM://内存分配错误
case SZ_ERROR_THREAD://线程错误
free(pInBuffer);
free(pOutBuffer);
return 2;
case SZ_ERROR_OUTPUT_EOF://缓冲区过小
free(pInBuffer);
free(pOutBuffer);
return 3;
}
}
int DecompressFile(FILE*fpOut,FILE*fpIn)
{
void*pSrcBuffer;
size_t InSize;
void*pDestBuffer;
size_t OutSize;
unsigned char Props;
fread(&OutSize,1,sizeof(OutSize),fpIn);//读取原数据大小
fread(&InSize,1,sizeof(InSize),fpIn);//读取压缩后的数据大小
pDestBuffer=malloc(OutSize);//分配内存
pSrcBuffer=malloc(InSize);//分配内存
if(!pSrcBuffer||!pDestBuffer)//内存不足
{
free(pSrcBuffer);
free(pDestBuffer);
return 2;
}
fread(Props,1,sizeof(Props),fpIn);
fread(pSrcBuffer,1,InSize,fpIn);
switch(LzmaUncompress(pDestBuffer,&OutSize,pSrcBuffer,&InSize,Props,sizeof(Props)))
{
case SZ_OK:
fwrite(pDestBuffer,1,OutSize,fpOut);
free(pDestBuffer);
free(pSrcBuffer);
return 0;
case SZ_ERROR_DATA:
case SZ_ERROR_UNSUPPORTED:
case SZ_ERROR_INPUT_EOF:
free(pDestBuffer);
free(pSrcBuffer);
return 1;
default:
case SZ_ERROR_MEM:
free(pDestBuffer);
free(pSrcBuffer);
return 2;
}
}然后就是测试的部分了。这里笔者就懒得专门跑去找文件进行测试了。我们就试试让它压缩Entry.c这个文件为Entry.lzma,然后再解压Entry.lzma到dec.txt。看看效果如何。
这应该是没有出错吧。哟西。那么我们来看看得到的文件。
嗯,我这边看了一下属性,Entry.lzma确实比Entry.c小很多。然后我们看看解压得到的dec.txt的内容。
经鉴定没有问题。接下来用WinHex看看Entry.lzma是个什么样子。
因为我的程序会在文件开头写入一些信息,因此第一个DWORD的值是解压后数据的大小,第二个DWORD的值是压缩后的数据的大小,然后5个字节是Props的值,后面的内容就是压缩包的内容了。总之LZMA的算法我懒得去研究了,所以这些压缩后的数据怎么解读这个我也不知道。感兴趣的可以去研究一下LzmaUncompress的实现。
如果你需要经常使用LZMA进行压缩解压,请抽个时间将其做成LIB静态库或者DLL动态库。这样就不用每次都复制粘贴这么多源码了。
LZMA的压缩和解压都比zlib慢,但是压缩率就比zlib高很多。大家可以根据情况来选择使用LZMA还是zlib。
个人感觉LZMA的接口挺方便的,比起zlib还要设置z_stream结构体来说,LZMA这种显得更直观。
EXE下载:
源码下载:
没人回帖?唉。。。
突然觉得自己可以发明一种新的图片格式:7png。我们平时见到的png图片都是用zlib压缩的。我弄一个LZMA压缩的png出来,估计一定比正常的png小很多吧?那么就把它起名为7png。一定很有意思。 这东西也可以拿来给网络传输的数据进行加密、压缩。 楼主,在不?
用你的方法,出现了一些bug诶,首先是有一些文件不能压缩,其次是,多个文件一起压缩时候,就不能解压了?
请问该怎么办呢?:) 请问楼主,这个缓冲区大小怎么定义呢?
压缩时最常见错误就是缓冲区过小呢 一个关键型问题,为什么我解压缩不成功呢,总是error:1
用的是vs2010 vc6.0 也试过了,一开始解压图片不成功,后来按照楼主的帖子上的Entry。c来压缩,成功,但是解压缩不成功呢,,,, 琦天大圣orz 发表于 2014-5-26 11:08
楼主,在不?
用你的方法,出现了一些bug诶,首先是有一些文件不能压缩,其次是,多个文件一起压缩时候,就 ...
我的这个实例只是用于压缩、解压单个文件,作为7z的使用范例。肯定不能压缩多个文件。想得美。 琦天大圣orz 发表于 2014-5-26 11:17
请问楼主,这个缓冲区大小怎么定义呢?
压缩时最常见错误就是缓冲区过小呢 ...
缓冲区过小是因为,那个文件已经没有压缩的必要了(因为它被压缩后体积反而膨胀了) 琦天大圣orz 发表于 2014-5-26 11:52
一个关键型问题,为什么我解压缩不成功呢,总是error:1
用的是vs2010 vc6.0 也试过了,一开始解压图片不成 ...
如果你直接下载我的LZMAComp的源码就会成功。我是试过的,都没有问题。
页:
[1]