如何在VC中使用7z SDK压缩文件

首先登陆7z官方组织网站https://www.7-zip.org/,英文不好的朋友可先切换至中文。

打开左边LZMA SDK页面,下载如下图最新SDK和说明文档。

下载好后,SDK包内容对多种编程语言进行了支持,这里只研究C++(即CPP),其他类似。

首先是对各种加解压缩模块的支持,对应如下目录:

关于这些模块,在lzma1900\DOC目录下的lzma-sdk.txt文件中,有如下一段说明:

  Bundles  - Modules that are bundles of other modules (files)

      Alone7z       - 7zr.exe: Standalone 7-Zip console program (reduced version)

      Format7zExtractR  - 7zxr.dll: Reduced version of 7z DLL: extracting from 7z/LZMA/BCJ/BCJ2.

      Format7zR         - 7zr.dll:  Reduced version of 7z DLL: extracting/compressing to 7z/LZMA/BCJ/BCJ2

      LzmaCon       - lzma.exe: LZMA compression/decompression

      LzmaSpec      - example code for LZMA Specification

      SFXCon        - 7zCon.sfx: Console 7z SFX module

      SFXSetup      - 7zS.sfx: 7z SFX module for installers

      SFXWin        - 7z.sfx: GUI 7z SFX module

 

这里之所以提到这些模块,是因为后面我们要用到他们编译dll,来给VC调用。

如这里我们用到的是D:\7z压缩\lzma1900\CPP\7zip\Bundles\Format7zR,SDK库中已经为我们提供了对应的makefile文件。我们只需要使用不同版本的VS中提供的nmake工具,来进行编译即可。

使用如上图VS的命令工具,直接使用cd命令进到对应目录下,nmake编译即可。

编译完成后如下:

如下图目录中,新编译出的如下7zra.dll,即为我们需要的供vc调用的动态库,虽然网上也提供了很多用ShellExcute去操作7z.exe等的命令实现方式,但是为了保证主程序只有一个执行文件,这里我没有采用这种方式。

有了动态库7zra.dll,接下来我们需要参阅D:\lzma1900\CPP\7zip\UI\Client7z下的示例工程,修整出我们的vc压缩库,用VS打开如下工作空间:

原始工程即可通过编译:

但提供的main函数中涉及了很多对命令行的解析和操作,有点冗长,我对很多地方做了一些简化和调整,整理出如下的一个通用函数。此外,我们需要明白7z sdk 关于压缩这一块的基本架构,既然是压缩过程,我们肯定需要提供两个东西,一是我们需要压缩哪些文件,这显然需要一个向量组,二则是压缩后的文件保存在哪里,这需要一个路径,因此,首先我们需要一个Vector向量组CObjectVector<CDirItem> &dirItems,它的每个项目其实就是用来存放我们需要压缩的某个文件的信息,它将被用于在CArchiveUpdateCallbackInit(&dirItems)时传入。此外,我们还需要一个最终压缩得到的*.7z的最终压缩文件的路径,该路径archiveName被用于在CArchiveUpdateCallback类构建时传入Create(archiveName, true)。

了解了压缩的过程,解压缩过程已经比较简单了,留给读者自行实现即可。

这里为了实现调试该项目,我们需要对该工程做如下位置的调整。

此外,别忘了把之前的库7zra.dll拷贝到生成的执行文件Client.exe目录下。

 

如下我对Client7z.cpp做了以下位置的一些调整,没有用到的部分直接注释和删除了。

另编写了如下的几个函数,来实现递归子级目录的压缩,简化了main函数的内容,注意以下我编写的是多字节版本的程序。

如需多字节和Unicode版本的完整源码工程,请在本文最后的资源链接中下载。

​​​​​​把他们导入自己的工程时,只要按照示例工程,在自己的解决方案中添加同样的需要用到的头文件和源文件即可,另注意需要在Client7z.cpp上面加上#include "CPP/Common/Common.h"【该路径根据实际情况】

//封装的获取压缩文件夹列表文件函数
bool GetArchiveItemFromPath(const char * strDirPath,const char * parentDirPath,CObjectVector<CDirItem> &dirItems)
{
	//注意这里提供了一个从const char * 转换到FString的函数CmdStringToFString,
    //但我没有用,而是直接用的库提供的宏来做转换

	NWindows::NFile::NFind::CFindFile findFile;
	NWindows::NFile::NFind::CFileInfo fileInfo;
	
	FString dirPath = fas2fs(strDirPath);

	bool bRet = findFile.FindFirst(dirPath + fas2fs("\\*.*"),fileInfo);
	if (bRet == false)return bRet;

	do				//递归遍历出所有的包含目录和文件并记录
	{
		if (fileInfo.IsDots())
		{
			continue;
		}

		CDirItem di;
		di.Attrib = fileInfo.Attrib;
		di.Size = fileInfo.Size;
		di.CTime = fileInfo.CTime;
		di.ATime = fileInfo.ATime;
		di.MTime = fileInfo.MTime;

	    //一定要赋值为压缩根路径下的全路径,否则不能压缩子目录内容及递归压缩子
         //目录的子目录下的内容,他们的内容将是空的

		if (parentDirPath != NULL)		
		{
			di.Name = fas2fs(parentDirPath) + fas2fs("\\") + fileInfo.Name;
		}
		else
		{
			di.Name = fileInfo.Name;
		}

		di.FullPath = dirPath + fas2fs("\\") + fileInfo.Name;
		dirItems.Add(di);

		if (fileInfo.IsDir())
		{
			FString parentPath;
			if (parentDirPath != NULL)
			{
				parentPath = fas2fs(parentDirPath) + fas2fs("\\") + fileInfo.Name;
			}
			else
			{
				parentPath = fileInfo.Name;
			}
			FString fFullPath = dirPath + fas2fs("\\") + fileInfo.Name;

            //递归所有的子级目录的内容
			GetArchiveItemFromPath(fs2fas(fFullPath),fs2fas(parentPath),dirItems);	
		}

	}while(findFile.FindNext(fileInfo));

	findFile.Close();

	return true;
}

DWORD ArchiveFile(CObjectVector<CDirItem> &dirItems,const char * ArchivePackPath)
{
	NT_CHECK

	NDLL::CLibrary lib;

    //加载7zra.dll库
	if (!lib.Load(NDLL::GetModuleDirPrefix() + FTEXT(kDllName)))
	{
		PrintError("Can not load 7-zip library");
		return 1;
	}

    //导出CreateObject函数指针
	Func_CreateObject createObjectFunc = (Func_CreateObject)lib.GetProc("CreateObject");	
	if (!createObjectFunc)
	{
		PrintError("Can not get CreateObject");
		return 1;
	}

	FString archiveName = fas2fs(ArchivePackPath);		//压缩文件路径

	COutFileStream *outFileStreamSpec = new COutFileStream;
	CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;

	if (!outFileStreamSpec->Create(archiveName, false))	  //创建压缩文件
	{
		PrintError("can't create archive file");
		return 1;
	}

	CMyComPtr<IOutArchive> outArchive;
    //构件压缩对象
	if (createObjectFunc(&CLSID_Format, &IID_IOutArchive, (void **)&outArchive) != S_OK)	
	{
		PrintError("Can not get class object");
		return 1;
	}

	CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
	CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
	updateCallbackSpec->Init(&dirItems);		//用待压缩项目组初始化压缩动作执行类

	HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);

	updateCallbackSpec->Finilize();

	if (result != S_OK)
	{
		PrintError("Update Error");
		return 1;
	}

	FOR_VECTOR (i, updateCallbackSpec->FailedFiles)
	{
		PrintNewLine();
		PrintError("Error for file", updateCallbackSpec->FailedFiles[i]);
	}

	if (updateCallbackSpec->FailedFiles.Size() != 0)    //判断有几个文件压缩失败
		return 1;

	return 0;
}
// Main function
#define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1;

int MY_CDECL main(int numArgs, const char *args[])
{
	numArgs = numArgs;	//纯粹消除编译警告,无任何意义
	args = args;
	CObjectVector<CDirItem> ItemList;
	GetArchiveItemFromPath("D:\\Mycomputer\\abc",NULL,ItemList);
	ArchiveFile(ItemList,"D:\\Mycomputer\\abc\\CPP.7Z");
  
	return 0;
}
//int compress(char * compressDestPath,char * compressSrcDir)
//{
//	CObjectVector<CDirItem> ItemList;

//	GetArchiveItemFromPath(compressSrcDir,NULL,ItemList);
//	ArchiveFile(ItemList,compressDestPath);

//	return 1;
//}

测试时我使用了如下的三级子目录结构,可以正常实现对其的压缩。

 

如需如下源码包研究的,可在下述链接中下载源码。【注意:打开工程时,务必打开sln后缀文件,打开dsw后缀的文件会导致我设置好的配置被还原】

多字节版本:https://download.csdn.net/download/tanjiaqi2554/11544912

Unicode版本:https://download.csdn.net/download/tanjiaqi2554/11545863

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
VC 使用内存映射技术可以显著提高对大文件的读写速度。下面是一个简单的示例代码: ```c++ #include <iostream> #include <windows.h> using namespace std; int main() { HANDLE hFile; HANDLE hMapping; LPVOID lpMapping; // 打开文件 hFile = CreateFile("largefile.bin", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { cout << "Failed to open file." << endl; return 1; } // 创建文件映射 hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (hMapping == NULL) { cout << "Failed to create file mapping." << endl; CloseHandle(hFile); return 1; } // 映射文件到内存 lpMapping = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (lpMapping == NULL) { cout << "Failed to map file to memory." << endl; CloseHandle(hMapping); CloseHandle(hFile); return 1; } // 读写文件 char* pFile = (char*)lpMapping; for (int i = 0; i < 4 * 1024 * 1024 * 1024LL; i++) { pFile[i] = i % 256; } // 解除内存映射 UnmapViewOfFile(lpMapping); // 关闭文件映射和文件句柄 CloseHandle(hMapping); CloseHandle(hFile); cout << "Done." << endl; return 0; } ``` 这个示例程序打开一个名为 largefile.bin 的文件,并将其映射到内存。然后,程序将对文件进行读写操作,最后解除内存映射并关闭文件。在这个示例,我们使用了一个 4GB 的文件进行测试,程序可以在较短的时间内完成读写操作,而不会出现内存不足的错误。 需要注意的是,内存映射虽然可以提高文件读写速度,但也会占用一定的内存空间。因此,在使用内存映射技术时,需要根据实际情况选择合适的文件大小和内存使用量。同时,需要注意内存映射可能会对程序的稳定性产生影响,特别是在进行大量读写操作时。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值