Обсуждение статьи "Работаем с ZIP-архивами средствами MQL5 без использования сторонних библиотек" - страница 8

 
Планируется ли добавление функции распаковки строки, сжатой алгоритмом GZIP?
 
Ivan Titov #:
Планируется ли добавление функции распаковки строки, сжатой алгоритмом GZIP?

Краткий ответ: нет, не планируется, так как это совсем другой формат не совместимый с zip. Для работы с GZip нужна отдельная, аналогичная этой, библиотека. Далее моя цитата из соседней ветки:

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

WebSocket и GZIP

Vasiliy Sokolov, 2024.03.21 14:35

Стоит различать форматы хранения данных от алгоритмов сжатия.

Zip - это формат хранения нескольких файлов в одном, наподобии формата TAR. Сами файлы в формате zip обычнно сжимаются алгоритмом сжатия deflate, но могут не сжиматся им вовсе, а просто хранится в этом формате, в таком случае формат позволяет просто объединить несколько файлов в один архив (файл) без сжатия.

GZip это формат хранения сжатых данных алгоритмом deflate в одном файле.

Если Вам выдали GZip - формат zip вам не подойдет. Вам нужно знать формат gzip и распоковать его с помощью системной функции CryptEncode(CRYPT_ARCH_ZIP). Это не сложно сделать самостоятельно, без привлечения сторонних библиотек, так как алгоритм сжатия там один и тот же, deflate (в MQL он имеет не очень удачный идентификатор CRYPT_ARCH_ZIP). Жаль что нет специальной статьи "Работаем с GZip архивом средствами MQL5". В целом, это не задача системной функции, а задача специальной MQL бибилотеки, оборачивающий deflate вокруг формата gzip.


 
Jacobie Nycambren Barksdale #:
Есть какие-нибудь новости по этому вопросу? Я сталкиваюсь с ошибками

'ZipLocalHeaderOpen' имеет конструктор и не может быть использован как член союза ZipHeader.mqh 52 23

Якоби, Станислав Короткий опубликовал пакет, содержащий эту библиотеку 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 не смог распаковать. При этом 7ZIP и архиватор Винды распаковывают без проблем.
Распечатал размер сжатых данных - оказалось на десятки мегабайт меньше архива (а в нем только 1 файл).

CompressedSize:76964920 
Начал искать где он расчитывается, и нашел в функции FindZipFileSize().

Поэкспериментировал...
Оказалось что если вернуть, как размер данных все данные end_size - то архив распаковывается правильно. Видимо при распаковке код сам определяет конец данных, а не полагается на ответ из этой функции. Главное, чтобы не было меньше.  Можно было оставить так, но получается, что функция бесполезна, а это вряд ли. И возможно какие-то другие архивы будут сбоить..
Еще 1 эксперимент показал, что если закоментировать строки

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

То архив тоже начинает распаковываться. Количество данных приблизилось к 100%

CompressedSize:106638447 
Получается, что в архиве имеется  uint cdheader =   0x504b0102; и это часть сжатых данных, а не метка их конца.

Вы не ошиблись с меткой? В интеренте поиском нашел такую метку. Может ее как то по другому надо обрабатывать, а не обрезать данные по ней, у меня обрезалось 30МБ.

Рабочая с этим файлом функция: (файл \Include\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;
   //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 byte equal 0x504b check all signatures
      if(pattern == header)
         break;
    //  if(pattern == cdheader)
    //     break;
   }
   //No signatures find. Bad format.
   if(size == end_size-1)
      return 0;
   //Return size - signature size.
   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;

В этой функции явно какая то недоработка. Ведь и условие

   //No signatures find. Bad format.
   if(size == end_size-1)
      return 0;

никогда не исполнится, т.к. при выходе из цикла по достижении конца данных, будет 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 =       0xffff0000;
   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 byte equal 0x504b check all signatures
//      if(pattern == header)
//         break;
//      if(pattern == cdheader)
//         break;
   }
   //No signatures find. Bad format.
   if(size == end_size-1)
      return 0;
   //Return size - signature size.
   return size-sizeof(ZIP_LOCAL_HEADER)+1;
   */
   return ArraySize(zip_array)-offset-sizeof(ZIP_LOCAL_HEADER)+1;
}

Программа с этой версией функции уже скачала 110 архивов на 12 Гг в сумме и всё успешно распаковала.

Если у кого будут проблемы с распаковкой - можете попробовать такой вариант функции.

 
Скачал и распаковал почти 300 файлов. А данных в них все больше и достиг предела по размеру.
Файл должен быть с 1,8 млрд char элементов, а распаковывается обрезанным до 1,5 млрд. Часть данных теряется. Странно, что так коротко обрезается, массивы могут иметь до 2147483647 элементов.
Обрезанные данные выдает терминальная функция
CryptDecode(CRYPT_ARCH_ZIP, m_file_puck, key, file_array);

С этим уже ничего не сделаешь...

Подумал, что можно ровными к кб/мб блоками декодировать - подавал 1024, 1024*1024 и 1024*1024*10 - не получилось.

Придется не распаковывать на лету, а сохранять архивы, потом вручную распаковывать и уже потом их обрабатывать. Неудобно будет - без автоматизации ((

Может как то Виндовый архиватор можно использовать? Через WinExec распаковать в файл, потом его построчно читать. Так автоматизация останется. Но не для маркета.
 
Forester #:
Может как то Виндовый архиватор можно использовать? Через WinExec распаковать в файл, потом его построчно читать.

Можно, это очевидно. А в чём проблема? Может я неправильно понял? Издавна есть консольные архиваторы UnRAR.exe, UnZip.exe и др.

 
Forester #:

С очередным файлом ошибка. На этот раз помогло комментирование строк

Т.е. код uint header =     0x504b0304; тоже встречается в содержимом архива и он успешно распаковывается 7Zip, Windows и этой исправленной версией CZip.

Т.к. оба выхода из цикла отключены, то и цикл стал лишним, его можно удалить и возвращать:

В этой функции явно какая то недоработка. Ведь и условие

никогда не исполнится, т.к. при выходе из цикла по достижении конца данных, будет size == end_size, а не на 1 меньше.

В итоге функцию укоротил до одной строки:

Программа с этой версией функции уже скачала 110 архивов на 12 Гг в сумме и всё успешно распаковала.

Если у кого будут проблемы с распаковкой - можете попробовать такой вариант функции.

Могу предположить, что метки все же нужно искать, но не в теле архива, а между файлами, если их несколько. Наверное должны быть где-то записаны длины архива...
В общем, мое решение частное под мою задачу с 1-м файлом в архиве, если их несколько - возможно нужно как-то по другому делать.

 
Forester #:

Могу предположить, что метки все же нужно искать, но не в теле архива, а между файлами, если их несколько. Наверное должны быть где-то записаны длины архива...
В общем, мое решение частное под мою задачу с 1-м файлом в архиве, если их несколько - возможно нужно как-то по другому делать.

Сделал распечатку сжатых и несжатых размеров которые должны быть в заголовках.
Те файлы, что я скачивал - имеют там 0 0. Если размер=0, то вызывается обсуждаемая выше FindZipFileSize()

Создал нормальным архиватором архив с 1-м файлом. Размеры в заголовке:
46389587 376516461

И еще архив с 2-мя файлами, включая тот, что был добавлен в первый архив:
46981880 314725045
46389587 376516461

Оба варианта имеют записанные в заголовках размеры и не вызывали FindZipFileSize()

А те файлы, что я скачивал (с 0 0 в размерах) - видимо создавались софтом, который не записал размеры в заголовки.

Возможно, мое решение укоротить FindZipFileSize() универсальное.

 
Edgar Akhmadeev #:

Можно, это очевидно. А в чём проблема? Может я неправильно понял? Издавна есть консольные архиваторы UnRAR.exe, UnZip.exe и др.

Сделал распаковку через 7-zip  (UnZip.exe c 2009г не обновлялся о поддержке даже Win 7 не написано).
Сравнил скорость:
Эта CZIP библиотека:

2025.06.14 20:59:06.758    Archive successfully opened. Total files: 1.
2025.06.14 20:59:07.345    UnZipped
587 мс

7-zip:
2025.06.14 21:00:07.312    Start unzip by 7-Zip.
2025.06.14 21:00:09.274    UnZipped by 7-Zip. File size: 428.22 MB
1962 мс

Это только на распаковку файла со сбросом на SSD диск. А еще надо читать файл с диска построчно.

Общее время от старта до окончания парсинга:
 Total Time: 10s 709ms
и
 Total Time: 12s 892ms
Разница 2 с 183 мс

В общем, для скорости предпочтительнее использовать эту CZIP библиотеку и если файл слишком большой, то использовать другие архиваторы.

Мне для ~1000 файлов по 2 сек = 33 минуты экономии. На самом деле больше, т.к. это пример с самым маленьким файлом на 428 мб, CZIP распаковывает файлы до ~1.7 Гб.
Все что больше (до 4 Гб) обрабатывает 7-zip. Так что 1-1.5 часа экономии есть.

Тестировал CZIP c моими правками - распаковал 600+ файлов на 100Гб без ошибок.