zoukankan      html  css  js  c++  java
  • windows下解压tar.gz文件

    最近做的一个项目,使用小工具代替人工提交到网站并获取结果。

    在开发过程中碰到了一个问题。

    网站返回的格式为tar.gz文件。第一个想到的方式是直接用第三方工具,使用的是7z。但用户那里使用的系统为win10,不以管理员身份运行会没有调用第三方工具的权限。遂决定用库解决该问题。

    这里记录下windows下解压tar.gz的一些问题。

    其实tar.gz是两次封装。

    第一次是tar打包(这里不会压缩,文件反而会更大),第二次是gz文件的算法压缩。

    解压GZ文件使用的是zlib库。

    这里说一下zlib的使用过程。

    一开始我是图懒直接从官网下载的windows develop版本,包含头文件,静态库和dll。但运行过程中出现了缺少zlib1.dll的问题。经过反复的折腾,没有解决。他这个库生成时间是2005年,可能在新版windows下运行会有问题把。

    本人对系统了解还是不多,没有找到深层次的原因。如果有大神知道原理请告知。

    在此之后下载了新版并进行编译。具体编译过程忘记了,挺顺利的。在vs命令行里执行一条命令后,会直接生成vs工程项目。选择静态库或动态库编译即可。

    不知道这里能不能发csdn的连接,我是根据这篇文章来编译的:http://blog.csdn.net/shellching/article/details/8116622。

    当然我用的版本是vs2015。用这种方法也能顺利通过。

    接下来就是解压了。一开始我从网上找到的资料全是compress和uncompress,后来发现:

    错啦!

    压根就不是这么解压的。

    具体原因没有详细了解到,大体上我的理解就是,其实gz文件需要一个头来标识压缩级别等信息。而compress和uncompress只是压缩字节流,单纯地调用uncompress而不知道数据的开始位置和数据的压缩信息的话,是无法正常压缩的。

    下面是解压代码:

    
    

    //初始化结构体

    z_stream strm = { 0 };

    strm.zalloc = Z_NULL;

    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;

    
    
    

    //下面分别初始化源数据,源数据长度,目标数据长度和目标数据地址

    strm.next_in = srcBuffer;
    strm.avail_in = srcLength;
    strm.avail_out = dstLength;
    strm.next_out = dstBuffer;

    //下面函数的第二个参数在头文件的函数说明里有,这里的这个参数意思是自动判断信息头
    int res = inflateInit2(&strm, MAX_WBITS + 32);

    //执行解压缩

    res = inflate(&strm, Z_NO_FLUSH);

    //回收资源
    inflateEnd(&strm);

    上面的代码我去掉了结果判断的部分,具体的结果判断大家参考头文件的信息把。我其实在代码里没有进行详细判断,只是简单判断了成功失败,并没有返回原因。这里就不贴上来坑大家了。

    有几点注意事项:

    1,目标内存长度需要足够。当然不够也没关系,该函数会在执行时发现并返回这个错误,在判断后逐步增加缓冲区即可。

    2,转换成功后目标数据的长度为:dstLen - strm.avail_out。

    3,如果你编译后使用这些函数,这个程序会莫名其妙地崩掉

    4,如果你是新手(现在没啥新手在VS上用这个了把。。。),可能会报无法连接的错误,当然你引入了lib还是会报。

    先说下为何会报连接错误,需要加一个宏ZLIB_WINAPI,如果有新手在看的话,跟大家分享下。

    一个库,无论是静态还是动态。在有头文件的时候,有两个地方需要注意,一是你编译库的时候,导出函数前面有没有加export,第二个是你引入库的时候,导出函数前面是不是import。

    有些库因为要处理跨平台的问题,库的源码的宏定义会非常复杂,这时候要仔细观察,设置响应的宏确定库函数的正确引入和导出,如果你用VS的话,VS会根据宏定义的状态来高亮具体代码用到哪个宏。

    在编译和引入的时候都要注意。

    接下来,在崩溃之后,各种google原因,但始终没有发现是为什么,因为当时没有解压成功,我也一度怀疑自己的代码有问题,或者库有问题。

    在这里,感谢一位大神:http://blog.csdn.net/u013283835/article/details/70311499

    真是跪谢。这是怎么发现的。祝您能修到孙艺珍。

    根据这位大神的说法,把预处理器中的ASMINF这个宏去掉。执行程序将转换出来的字节流写入文件。搞定了。

    这里鄙视下CSDN:本人的csdn因为前几年给别人发QSS代码被封了。我也是醉了,发个QSS代码都能封。就这还搞技术论坛。呵呵。

    接下来就进行tar文件的解析了。最开始想到的使用libarchive库。

    嗯。编译,连接,导入,使用。在经历过种种挫折后,我突然发现:libarchive不支持全路径,只能打开当前工作目录下的文件,当然也可能是我某个配置没有配好,但libarchive的资料实在是太少了。

    官方文档也只有很粗略的解释。而且在win10下,如果程序安装在C盘program files下,运行程序是没有创建文件权限的,在其他盘则需要不停设置当前路径。放弃。这个库真不是一般的难用。

    查了下tar文件的格式:

    http://blog.chinaunix.net/uid-20357359-id-1963469.html

    https://www.gnu.org/software/tar/manual/html_node/Standard.html

    嗯,貌似做简单的拆包不复杂吗。外加我做的小项目中tar文件本就是自动生成的,只有一级目录。自己解决把。

    下面是代码:

     

    typedef struct posix_header

    {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char chksum[8];
    char typeflag;
    char linkname[100];
    char magic[6];
    char version[2];
    char uname[32];
    char gname[32];
    char devmajor[8];
    char devminor[8];
    char prefix[155];
    }TAR_HEADER;

    //参数为目标路径、tar缓冲区,缓冲区长度
    void
    ExtractTar(const char *dstDir, BYTE *buffer, uLongf len) { uLongf index = 0; while (true) { if (index == len - 1) { return; } TAR_HEADER newHeader = { 0 }; memcpy(&newHeader, buffer + index, sizeof(TAR_HEADER)); index += sizeof(TAR_HEADER); if (strcmp(newHeader.name, "") == 0) { return; } char fileFullPath[MAX_PATH] = { 0 }; memcpy(fileFullPath, dstDir, strlen(dstDir)); memcpy(fileFullPath + strlen(dstDir), newHeader.name, strlen(newHeader.name)); FILE *newFile = NULL;      fopen_s(&newFile, fileFullPath, "wb+");long size = strtol(newHeader.size, (char **)(&(newHeader.size) + 12), 8); size_t res = fwrite(buffer + index, 1, size, newFile); fclose(newFile); index += size;

         //填充字节块
    int nblock = index / 512 + 1; index = nblock * 512; } }

    注意:

    1,我还是去掉了返回判断的代码,如果想完成健壮代码请判断各种状态返回值。

    2,这段代码不能解压包含文件夹或者多级目录的tar文件,也不能解压特殊文件,也没有判断字节偏移等数据。

    简单来说,这段代码只能保证我这个小项目的需求。而且很大程度上满足不了其他需求。

    经过上面两步,基本上满足了我的需求。在这里分享给大家。

  • 相关阅读:
    第二次结对作业(陆桂莺+崔亚明)
    第一次结对作业
    第二次作业:代码互改
    markdown详细
    第一次个人编程作业:我的分数我做主
    手动下载transformers的模型
    torch设置GPU
    Python import的搜索路径和不可以import的解决方法 (On Linux)
    Python中windows路径的3种写法
    一台计算机安装多个版本的torch和CUDA的教程
  • 原文地址:https://www.cnblogs.com/xiaoBay/p/7567249.html
Copyright © 2011-2022 走看看