文章 "单纯使用 MQL5 语言处理 ZIP 档案" - 页 8

 
是否计划添加解压用 GZIP 算法压缩的字符串的功能?
 
Ivan Titov CryptEncode(CRYPT_ARCH_ZIP) 系统函数对其进行解压缩。在不使用第三方库的情况下,自己解压缩并不困难,因为压缩算法是一样的,都是 deflate(在 MQL 中,它有一个不太好的标识符CRYPT_ARCH_ZIP)。遗憾的是,没有专门的文章 "使用 MQL5 工具处理 GZip 压缩包"。一般来说,这不是一个系统函数的任务,而是一个特殊的 MQL bibiloteka 的任务,将 deflate 包装成 gzip 格式。
 
Jacobie Nycambren Barksdale #:
有什么最新进展吗?我遇到了错误

ZipLocalHeaderOpen' 有构造函数,不能用作联合成员 ZipHeader.mqh 52 23

Jacobie, Stanislav Korotky 在这里发布了一个 包含该 zip 库的软件包,并做了一些修复。

我已经对其进行了测试,目前运行得还不错。

MQL5 Program Packer
MQL5 Program Packer
  • www.mql5.com
This is MQL5 project packer: assemble all source and resource files from dependencies into a single ZIP.
 

我遇到一个 CZip 无法解压的压缩包。
打印出压缩数据的大小 - 结果比压缩包小几十兆字节(其中只有一个文件)。

CompressedSize:76964920 
我开始寻找计算的地方,结果在 FindZipFileSize() 函数中找到了。

经过实验......
结果发现,如果将所有 end_size 数据作为数据大小返回,压缩包就能正确解压缩。显然,在解压缩时,代码本身会确定数据的结束,而不是依赖该函数的响应。主要问题是,它不应该更小。 你可以让它保持这样,但结果是该函数毫无用处,这不太可能。也许还有其他存档会失败......
还有一个实验表明,如果注释掉以下几行

    // if(pattern == cdheader) 
    // break;

存档也会开始解包。数据量接近 100%。

CompressedSize:106638447 
原来,压缩包中有 uint cdheader =0x504b0102;这是压缩数据的一部分,而不是其结尾的标签。

你是不是弄错了标签?我在网上搜索到过这样的标签。也许应该用其他方法来处理它,而不是用它来剪切数据,我剪切了 30MB。

Function working with this file: (file\Include\Zip\Zip\Zip.mqh)

int CZip::FindZipFileSize(uchar &zip_array[],int offset)
{
   uint pattern =    0;
   int size =        0;
   uint header =     0x504b0304;
   uint cdheader =   0x504b0102;
   uint mask =       0xffff0000;
   int end_size = ArraySize(zip_array)-offset;
   //这是基于字节左移的环形缓冲区: x = x << 8
   for(; size < end_size; size++)
   {
      pattern = pattern << 8;
      uint nbyte = zip_array[offset+size];
      pattern = pattern | nbyte;
      //检查上面 2 个字节
      if((pattern & mask)!=(0x504b << 16))
         continue;
      //如果上面两个字节等于 0x504b 检查所有签名
      if(pattern == header)
         break;
    // if(pattern == cdheader)
    // break;
   }
   //未找到签名。格式错误。
   if(size == end_size-1)
      return 0;
   //Return 尺寸 - 签名尺寸。
   return size-sizeof(ZIP_LOCAL_HEADER)+1;
}
如果你有兴趣弄明白,我可以通过私人信息把存档文件发给你。
 

另一个文件又出错了。这次注释行帮了大忙

// if(pattern == header)
// break;

即代码 uint header = 0x504b0304; 也出现在存档内容中,并被 7Zip、Windows 和此更正版本的 CZip 成功解压缩。

由于两个循环退出都被禁用,循环已变得多余,可以删除并返回:

return ArraySize(zip_array)-offset-sizeof(ZIP_LOCAL_HEADER)+1;

这个函数显然存在缺陷。毕竟,条件

   //未找到签名。格式错误。
   if(size == end_size-1)
      return 0;

条件永远不会满足,因为当数据结束时退出循环,size == end_size 将是 size == end_size,而不是少 1。

因此,我将函数缩短为一行:

int CZip::FindZipFileSize(const uchar &zip_array[],int offset)
{
   /*uint pattern = 0;
 int size =0;
 uint header = 0x504b0304;
 uint cdheader =0x504b0102;
 uint mask = 0xffffff0000;
 int end_size = ArraySize(zip_array)-offset;
 //this is ring buffer based on byte left shift:x = x << 8
 for(; size < end_size; size++)
 {
 pattern = pattern << 8;
 uint nbyte = zip_array[offset+size];
 pattern = pattern | nbyte;
 //check upper 2 bytes
 if((pattern & mask)!=(0x504b << 16))
 continue;
 //if two upper bytes equal 0x504b check all signatures
// if(pattern == header)
//break;
// if(pattern == cdheader)
//break;
 }
 // 没有找到签名。
 if(size == end_size-1)
 return 0;
 //Return size - signature size.
 return size-size-sizeof(ZIP_LOCAL_HEADER)+1;
 */
   return ArraySize(zip_array)-offset-sizeof(ZIP_LOCAL_HEADER)+1;
}



如果有人在解包时遇到问题,可以试试这个版本的函数。

 
下载并解压了近 300 个文件。其中的数据越来越大,已经达到了大小限制。
文件应该有 18 亿个字符元素,但解压缩后只剩下 15 亿个。 一些数据丢失了。
终端函数
生成剪切数据。
CryptDecode(CRYPT_ARCH_ZIP, m_file_puck, key, file_array);

对此没有任何办法...

我认为可以用偶数 kb/mb 块进行解码 - 我提供了 1024、1024*1024 和 1024*1024*10,但没有成功。

我必须先保存存档,然后手动解压缩,再进行处理。如果没有自动化,这将会很不方便((

有什么办法可以使用 Windows 存档器吗?使用 WinExec 解压到文件中,然后逐行读取。这样就能保持自动化。但不适合市场。
 
Forester #:
有什么办法可以使用 Windows 存档程序吗?使用 WinExec 解压到文件,然后逐行读取。

显然可以。问题出在哪里?也许我理解错了?很早以前就有 UnRAR.exe、UnZip.exe 等控制台压缩器了。

 
Forester #:

另一个文件又出错了。这一次,注释行帮了大忙

即代码 uint header = 0x504b0304; 也出现在存档内容中,并被 7Zip、Windows 和修正版 CZip 成功解压缩。

由于两个循环退出都被禁用,循环已变得多余,可以删除并返回:

这个函数显然存在缺陷。毕竟,条件

条件永远不会满足,因为在数据结束时退出循环时,size == end_size 将是 size == end_size,而不是少 1。

因此,我将函数缩短为一行:



如果有人在解包时遇到问题,可以试试这个版本的函数。

我认为您仍然需要搜索标签,但不是在存档正文中,而是在文件之间(如果有多个文件)。可能应在某处记录存档长度....。
一般来说,我的解决方案是针对存档中只有一个文件的任务而设计的,如果存档中有多个文件--也许你需要做其他事情。

 
Forester #:

我可以假定标签仍然需要搜索,但不是在存档正文中,而是在文件之间,如果有几个文件的话。可能应在某处记录存档的长度....。
总的来说,我的解决方案对我的任务来说是私人的,只需在存档中搜索一个文件,如果有多个文件,也许就需要做其他事情了。

我打印了压缩和未压缩的大小,这些大小应该出现在文件头中。
我下载的那些文件 - 大小为 0 0。如果 size=0,则调用上面讨论的 FindZipFileSize()。

我使用普通压缩器将第一个文件创建了一个压缩包。文件头中的大小:
46389587 376516461

另一个存档包含另外 2 个文件,包括添加到第一个存档中的文件:
46981880 314725045
46389587 376516461

这两个文件的大小都写在文件头中,并且没有调用 FindZipFileSize()

而我下载的文件(大小为 0 0)显然是由没有在文件头中写入大小的软件创建的。

也许我缩短 FindZipFileSize() 的解决方案是通用的。

 
Edgar Akhmadeev #:

我们当然可以。有什么问题吗?也许我理解错了?很早以前就有 UnRAR.exe、UnZip.exe 等控制台存档程序了。

我通过 7-zip 进行了解压(UnZip.exe 自 2009 年以来就没有更新过,甚至 Win 7 的支持也没有写)。
我比较了一下速度:
这个 CZIP 库:

2025.06.14 20:59:06.758 存档成功打开。文件总数:1。
2025.06.14 20:59:07.345 未压缩
587ms

7-zip:
2025.06.14 21:00:07.312 通过 7-Zip 开始解压缩。
2025.06.14 21:00:09.274 通过 7-Zip 解压缩。文件大小:428.22 MB
1962 ms

这只是将文件解压缩并重置到固态硬盘磁盘。你还必须从光盘中逐行读取文件。

解析从开始到结束的总时间:
总时间:10 秒 709ms

总时间:12 秒 892ms
相差 2 秒 183 毫秒。

一般来说,为了提高速度,最好使用这个 CZIP 库,如果文件太大,可以使用其他存档器。

对我来说,以每个文件 2 秒的速度处理约 1000 个文件,可节省 33 分钟。实际上更多,因为这是一个最小文件为 428 MB 的例子,CZIP 解压缩的文件最大可达 ~1.7GB。
更大的文件(最多 4GB)则由 7-zip 处理。因此可以节省 1-1.5 个小时。

我用自己编辑的文件对 CZIP 进行了测试,解压了 100GB 的 600 多个文件,没有出现任何错误。