在Windows下C++实现UNIX中的GZ格式的解压缩(附工具)

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

 

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

 

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

image

 

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

image

 

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

image

 

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

 

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

GZHelper.h 头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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可以移动指针到下一个参数中,从而遍历可变参数的值。

 

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

头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//目录选择类
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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* 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 */

 

如何调用:

1
2
3
4
5
6
7
8
CFolderDialog folderDialog("压缩到目录:");
 
int result = folderDialog.DoModal(this->m_hWnd);
 
if(result == 1)
{
    ...
}

 

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

 

希望对大家有所帮助!

posted @   Leepy  阅读(6571)  评论(2编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
点击右上角即可分享
微信分享提示