- UID
- 1
- 精华
- 积分
- 76365
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
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[1],"-c"))//压缩一个文件
- {
- FILE*fp=fopen(argv[2],"rb");
- FILE*fpout=fopen(argv[3],"wb");
- int iRet;
- unsigned long fLen;
- if(!fp)
- {
- fprintf(stderr,"Unable to open %s\n",argv[2]);
- return 2;
- }
- if(!fpout)
- {
- fprintf(stderr,"Unable to write %s\n",argv[3]);
- 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[3]);
- return iRet;
- }
- if(!stricmp(argv[1],"-d"))//解压一个文件
- {
- FILE*fp=fopen(argv[2],"rb");
- FILE*fpout=fopen(argv[3],"wb");
- int iRet;
- if(!fp)
- {
- fprintf(stderr,"Unable to open %s\n",argv[2]);
- return 2;
- }
- if(!fpout)
- {
- fprintf(stderr,"Unable to write %s\n",argv[3]);
- return 2;
- }
- iRet=DecompressFile(fpout,fp);
- if(iRet)
- fprintf(stderr,"Error:%d\n",iRet);
- fclose(fpout);
- fclose(fp);
- if(iRet)
- unlink(argv[3]);
- return iRet;
- }
- Usage();
- return 1;
- }
- int CompressFile(FILE*fpOut,FILE*fpIn,unsigned long InSize)
- {
- void*pInBuffer;//输入缓冲区
- void*pOutBuffer;//输出缓冲区
- unsigned long OutSize;//输出缓冲区大小
- unsigned char Props[LZMA_PROPS_SIZE];//属性
- 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[LZMA_PROPS_SIZE];
- 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下载:
LZMAComp.exe
(80 KB, 下载次数: 2, 售价: 1 个宅币)
源码下载:
LZMAComp.7z
(75.13 KB, 下载次数: 17, 售价: 10 个宅币)
|
|