使用lzma SDK对7z文件简单解压缩
有时候我们只需要单纯对lzma算法压缩的7z文件进行解压,有时需要在嵌入式设备上解压,使用p7zip虽然支持多种格式,但是不容易裁剪,使用lzma SDK是首选:
可以在这里找到各种版本:http://zh.sourceforge.jp/projects/sfnet_sevenzip/releases/
我下载了4.65版本,这个对文件名编码支持没有9.20的好,中文可能有问题,但是我的需求不需要支持中文文件名,所以足够用了。
解压后先看一下7z这个工程,这个示例只有文件解压操作,仿照就可以写一个更加精简的解压函数:
需要的文件可以参考实例:
修改7zMain.c即可。
我们的目的是写一个函数extract7z,接收参数是7z文件路径,输出文件路径,便可执行全部解压。
主要调用函数:
SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); SRes SzAr_Extract( const CSzArEx *p, ILookInStream *inStream, UInt32 fileIndex, UInt32 *blockIndex, Byte **outBuffer, size_t *outBufferSize, size_t *offset, size_t *outSizeProcessed, ISzAlloc *allocMain, ISzAlloc *allocTemp);</div>
我们先在Windows下编译:
完整代码如下:
/* 7zMain.c - Test application for 7z Decoder */ #include <stdlib.h> #include <stdio.h> #include <string.h> #define LOGD printf #define LOGE printf #include "7zCrc.h" #include "7zFile.h" #include "7zVersion.h" #include "7zAlloc.h" #include "7zExtract.h" #include "7zIn.h" int MY_CDECL extract7z(const char* srcFile, const char* dstPath) { CFileInStream archiveStream; CLookToRead lookStream; CSzArEx db; SRes res; ISzAlloc allocImp; ISzAlloc allocTempImp; char outPath[1024] = { 0 }; LOGD("7z ANSI-C Decoder " MY_VERSION_COPYRIGHT_DATE "\n"); if (InFile_Open(&archiveStream.file, srcFile)) {//open 7z file LOGE("can not open input file\n"); return 1; } FileInStream_CreateVTable(&archiveStream); LookToRead_CreateVTable(&lookStream, False); lookStream.realStream = &archiveStream.s; LookToRead_Init(&lookStream); allocImp.Alloc = SzAlloc; allocImp.Free = SzFree; allocTempImp.Alloc = SzAllocTemp; allocTempImp.Free = SzFreeTemp; CrcGenerateTable(); SzArEx_Init(&db); res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp); if(res == SZ_OK) { Int32 i; UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */ size_t outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ LOGD("Total file/directory count[%d]\n", db.db.NumFiles); for (i = db.db.NumFiles - 1; i >= 0; i--) { size_t offset; size_t outSizeProcessed; CSzFileItem *f = db.db.Files + i; strcpy(outPath, dstPath); strcat(outPath, "/"); strcat(outPath, f->Name); if (f->IsDir) { //dir LOGD("dir [%s]\n", outPath); mkdir(outPath); continue; }else{ //file LOGD("file [%s]\n", outPath); res = SzAr_Extract(&db, &lookStream.s, i, &blockIndex, &outBuffer, &outBufferSize, &offset, &outSizeProcessed, &allocImp, &allocTempImp); if (res != SZ_OK){ break; }else{ CSzFile outFile; size_t processedSize; if (OutFile_Open(&outFile, outPath)) { LOGE("can not open output file\n"); res = SZ_ERROR_FAIL; break; } processedSize = outSizeProcessed; if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 || processedSize != outSizeProcessed) { LOGE("can not write output file\n"); res = SZ_ERROR_FAIL; break; } if (File_Close(&outFile)) { LOGE("can not close output file\n"); res = SZ_ERROR_FAIL; break; } } } } IAlloc_Free(&allocImp, outBuffer); } SzArEx_Free(&db, &allocImp); File_Close(&archiveStream.file); if (res == SZ_OK) { LOGD("Everything is Ok\n"); return 0; } if (res == SZ_ERROR_UNSUPPORTED ) LOGE("decoder doesn't support this archive\n"); else if (res == SZ_ERROR_MEM ) LOGE("can not allocate memory\n"); else if (res == SZ_ERROR_CRC ) LOGE("CRC error\n"); else LOGE("ERROR #%d\n", res); return 1; } int main(int numargs, char *args[]) { return extract7z(args[1], args[2]); }</div>
我用的是Eclipse,使用Mingw编译。
执行效果,能正确解压。
这样的解压只能适用简单的解压,不支持加密,参数2的输出文件路径中的所有文件夹都必须存在,压缩包中文件夹不需要存在,解压时会自动创建。
压缩包中的文件夹不能为中文,否则乱码。
使用MD5算法验证文件完整性或密码正确性
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。
将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。
MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式(就是把一个任意长度的字节串变换成一定长的十六进制数字串)。
MD5在实际应用中通常有两种用法,一种是计算一个字符串的MD5值,常用于密码相关的操作;另一种是用于计算一个文件的MD5值,一般用于网络传输中验证文件是否出错。
下面是C语言的MD5计算程序,来自Stardict,网上流行的代码都大同小异:
md5.h
#ifndef MD5_H #define MD5_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STDINT_H #include <stdint.h> typedef uint32_t uint32; #else /* A.Leo.: this wont work on 16 bits platforms ;) */ typedef unsigned uint32; #endif #define MD5_FILE_BUFFER_LEN 1024 struct MD5Context { uint32 buf[4]; uint32 bits[2]; unsigned char in[64]; }; void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); void MD5Transform(uint32 buf[4], uint32 const in[16]); int getBytesMD5(const unsigned char* src, unsigned int length, char* md5); int getStringMD5(const char* src, char* md5); int getFileMD5(const char* path, char* md5); /* * This is needed to make RSAREF happy on some MS-DOS compilers. */ typedef struct MD5Context MD5_CTX; #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* !MD5_H */</div>
源文件:
md5.c
#include <string.h> /* for memcpy() */ #include <stdio.h> #include "md5.h" #ifndef HIGHFIRST #define byteReverse(buf, len) /* Nothing */ #else void byteReverse(unsigned char *buf, unsigned longs); #ifndef ASM_MD5 /* * Note: this code is harmless on little-endian machines. */ void byteReverse(unsigned char *buf, unsigned longs) { uint32 t; do { t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsig