zoukankan      html  css  js  c++  java
  • 在Windows下C++实现UNIX中的GZ格式的解压缩(附工具)

          今天在做项目中遇到一个问题,项目中需要开发一个PC工具(要求是Windows),其中需要将一些文件打包成gz文件,gz文件是UNIX系统中的压缩文件,后来找了找网上的资源,只有解压的C++源码,没有告诉你如何进行GZ格式的压缩,当然了,你还可以使用7Z软件对文件进行GZ解压缩。而本篇文章将用另外一个思路去实现GZ格式的解压缩。

    首先,创建一个C++的工程项目,这里使用MFC窗体项目。

    功能很简单,先看下整个窗体设计:

    image

    上面一排通过“选择文件”在下面的列表中显示文件路径,然后通过“压缩”按钮,保存到指定的目录中。

    image

    下面一排通过”选择文件”选择gz格式的压缩包,然后通过“解压”按钮,保存到指定的目录中。、

    image

    界面的功能就是这样,下面我来介绍下一些主要核心的功能。

    首先,来看GZ的一个处理类:

    GZHelper.h 头文件

    //GZ解压缩类(配合tar.exe的可执行文件)
    class GZHelper
    {
    public:
    	GZHelper();
    	virtual ~GZHelper();
    	static void Compress(char* gzFilePath, int fileNum, char* file, ...); //压缩,(压缩包路径,文件个数,可变参数的文件列表)
    	static void Compress(char* gzFilePath, int fileNum, char** files); //压缩,(压缩包路径,文件个数,文件列表)
    	static void Decompress(char* folderPath, char* gzFilePath); //解压,(解压目录, 压缩包路径)
    
    private:
    	static CString ConvertToUnix(CString winFile); //将Window上的路径格式转换为UNIX路径格式
    	static void FindFile(CString path, CString outPath); //遍历目录,并将目录中的所有文件移动到outPath中
    };

    GZHelper.cpp

    void GZHelper::Compress(char* gzFilePath, int fileNum, char* file, ...)
    {
    	va_list argptr;
    
    	va_start(argptr, gzFilePath);
    	va_arg(argptr, int);
    	
    	char** files;
    	files = new char*[fileNum];
    	for(int index = 0; index < fileNum; index++)
    	{
    		char* file = va_arg(argptr, char*);
    		CString str_file;
    		str_file = file;
    
    		files[index] = new char[str_file.GetLength()];
    		memcpy(files[index], file, str_file.GetLength());
    		files[index][str_file.GetLength()] = 0;
    	}
    	va_end(argptr);
    
    	Compress(gzFilePath, fileNum, files);
    }
    
    void GZHelper::Compress(char* gzFilePath, int fileNum, char** files)
    {
    	CString str_gzFilePath(gzFilePath);
    	CString folderPath = str_gzFilePath.Left(str_gzFilePath.ReverseFind('\\') + 1);
    	CString command = "cd ";
    	command = command + Path::StartupPath() + "tar && " + Path::GetDrive() + " && tar.exe zcPf ";
    	CString unix_str_gzfile = ConvertToUnix(str_gzFilePath);
    	command = command + "\"" + unix_str_gzfile + "\" ";
    	for(int index = 0; index < fileNum; index++)
    	{
    		char* file = files[index];
    		CString str_file;
    		str_file = file;
    		CString unix_str_file = ConvertToUnix(str_file);
    		command = command + "\"" + unix_str_file + "\" ";
    	}
    	
    	//执行命令
    	system(command);
    }
    
    void GZHelper::Decompress(char* folderPath, char* gzFilePath)
    {
    	CString str_folderPath(folderPath);
    	CString str_gzFilePath(gzFilePath);
    	CString command = "cd ";
    	command = command + Path::StartupPath() + "tar && " + Path::GetDrive() + " && tar.exe zxvf ";
    	CString unix_str_gzfile = ConvertToUnix(str_gzFilePath); 
    	command = command + "\"" + unix_str_gzfile + "\" ";
    
    	system(command);
    
    	CString outPath = str_folderPath + "\\demo";
    	CreateDirectory(outPath, NULL);
    
    	CString inPath = Path::StartupPath() + "tar\\cygdrive";
    	GZHelper::FindFile(inPath, outPath);
    
    	RemoveDirectory(inPath);
    }
    
    // 将Windows下的路径转换为UNIX路径
    CString GZHelper::ConvertToUnix(CString winFile)
    {
    	CString unixFile;
    	unixFile = winFile;
    	unixFile.Replace("\\", "/");
    	unixFile = "/cygdrive/" + unixFile.Mid(0, 1) + unixFile.Mid(2, unixFile.GetLength() - 2);
    	return unixFile;
    }
    
    void GZHelper::FindFile(CString path, CString outPath)
    {
    	CString szDir = path + "\\*.*";
    
    	CFileFind fileFind;
    	BOOL result = fileFind.FindFile(szDir);
    
    	while(result)
    	{
    		result = fileFind.FindNextFile();
    
    		if(fileFind.IsDots())
    			continue;
    
    		if(fileFind.IsDirectory())
    		{
    			GZHelper::FindFile(fileFind.GetFilePath(), outPath);
    		}
    		else
    		{
    			//移动文件
    			MoveFile(fileFind.GetFilePath(), outPath + "\\" + fileFind.GetFileName());
    		}
    	}
    
    	fileFind.Close();
    }

    通过代码中,我们看到两个方法Compress和Decompress,这里就是作为最核心的函数。

    实际上,原理就是通过windows上的命令提示符cmd去调用一个tar的在Windows下编译好的一个命令包,这个包的目录内容如下:

    image

    实际上它是利用cygwin1.dll组件,将UNIX上的tar命令转换到Windows平台上运行。

    这个包我会在连同工具和源码稍后在文章末尾一起奉上。

    我们看到,在Compress中我们使用到"cd”命令符,这里是需要将cmd当前的路径设置到应用程序里面的一个tar包的路径上。

    “&&”符号可以在单条指令中复合执行。

    注意这里的command,在字符串路径中最好需要用"\""将字符串隔开,这是为了防止字符串中的路径包括空格字符

    system函数执行cmd命令。

    另外,我们看到ConverToUnix函数,它是用来表示将Windows下的路径转换为cygwin下的虚拟UNIX路径:

    这是什么意思呢?现在我打开一个cygwin.exe工具,执行df命令:

    image

    可以看到,每个磁盘上的路径都已经对应了在cygwin中特定的虚拟路径,如D: 对应 /cygdrive/d

    ConverToUnix方法就是要将磁盘上的路径转换为cygwin可识别的虚拟路径下面。

    在tar.exe中对简单的压缩以及解压的指令,具体可以参考:http://www.21andy.com/blog/20060820/389.html

    大家也许注意到:static void Compress(char* gzFilePath, int fileNum, char* file, ...);

    这种写法很有趣,这个表示是一个可变形参的方法。后面“…”号,可以有任意个的参数表示,这样做的目的,是为了可以对任意多个文件进行压缩。在方法中,通过va_list,va_start,va_arg,va_end,va_list作为一个参数的指针通过va_arg可以移动指针到下一个参数中,从而遍历可变参数的值。

    另外,我在工具中添加了一个目录选择的类:

    头文件:

    //目录选择类
    class CFolderDialog
    {
    public:
    	CFolderDialog(
    		LPCTSTR title, 
    		DWORD dwFlags = BIF_STATUSTEXT | BIF_USENEWUI | BIF_RETURNONLYFSDIRS);
    	virtual ~CFolderDialog();
    
    	virtual INT_PTR DoModal(HWND hwnd);
    	CString GetPathName() const;
    
    private:
    	BROWSEINFO browseInfo;
    	CString m_path;
    };

    cpp文件:

    /* CFolderDialog Begin */
    CFolderDialog::CFolderDialog(LPCTSTR title, DWORD dwFlags)
    {
    	char szDir[MAX_PATH];
    
        ITEMIDLIST *pidl;
        //
        
        browseInfo.lpszTitle = title;
        browseInfo.ulFlags = dwFlags;
        
    }
    
    CFolderDialog::~CFolderDialog()
    {
    
    }
    
    INT_PTR CFolderDialog::DoModal(HWND hwnd)
    {
    	char szDir[MAX_PATH];
    	ITEMIDLIST *pidl;
    	browseInfo.pidlRoot = NULL;
        browseInfo.pszDisplayName = szDir;
    	browseInfo.hwndOwner = hwnd;
    
    	browseInfo.lpfn = NULL;
        browseInfo.lParam = 0;
        browseInfo.iImage = 0;
        pidl = SHBrowseForFolder(&browseInfo);
        if(pidl == NULL)  
    		return 2;
        if(!SHGetPathFromIDList(pidl, szDir))	
    		return 2;
    
    	m_path = szDir;
    	return 1;
    }
    
    CString CFolderDialog::GetPathName() const
    {
    	return m_path;
    }
    
    /* CFolderDialog End */

    如何调用:

    CFolderDialog folderDialog("压缩到目录:");
    
    int result = folderDialog.DoModal(this->m_hWnd);
    
    if(result == 1)
    {
        ...
    }

    最后附上该工具的源代码(内含tar命令包):GZCompressDemo.rar

    希望对大家有所帮助!

  • 相关阅读:
    try-catch- finally块中, finally块唯一不执行的情况是什么?
    Mac下 pygame.image.load不显示图片解决方法
    Ubuntu中sudo速度慢的解决方法(转载)
    [Mac Terminal] ___中断跳出死循环
    Fuzzy logic
    Discrete Mathematics and Its Applications | 1 CHAPTER The Foundations: Logic and Proofs | 1.4 Predicates and Quantifiers
    Bugs -----The largest and most interesting kind of thing.
    A Brief, Incomplete, and Mostly Wrong History of Programming Languages
    MIPS指令集
    >> 计算机的数据表示
  • 原文地址:https://www.cnblogs.com/liping13599168/p/2056882.html
Copyright © 2011-2022 走看看