Discusión sobre el artículo "Trabajamos con archivos ZIP con los medios de MQL5, sin usar bibliotecas ajenas" - página 8

 
¿Está previsto añadir una función para descomprimir una cadena comprimida con el algoritmo GZIP?
 
Ivan Titov CryptEncode(CRYPT_ARCH_ZIP). No es difícil hacerlo tú mismo, sin usar librerías de terceros, porque el algoritmo de compresión es el mismo, deflate (en MQL tiene un identificador no muy bueno CRYPT_ARCH_ZIP). Es una lástima que no exista un artículo especial "Trabajar con archivo GZip usando herramientas MQL5". En general, esta no es la tarea de una función del sistema, sino la tarea de una bibiloteka MQL especial, envolviendo deflate alrededor del formato gzip.
 
Jacobie Nycambren Barksdale #:
¿Alguna novedad al respecto? Me encuentro con errores

ZipLocalHeaderOpen' tiene constructor y no se puede utilizar como miembro de la unión ZipHeader.mqh 52 23

Jacobie, Stanislav Korotky ha publicado un pack que contiene esta librería zip aquí, con algunas correcciones.

Lo he probado, y de momento funciona bastante bien.

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.
 

Me encontré con un archivo que CZip no podía descomprimir. Al mismo tiempo 7ZIP y Windows archiver descomprimen sin problemas.
Imprime el tamaño de los datos comprimidos - resultó ser decenas de megabytes menos que el archivo (y sólo hay 1 archivo en ella).

CompressedSize:76964920 
Empecé a buscar dónde se calcula, y lo encontré en la función FindZipFileSize().

Experimenté...
Resultó que si devuelves todos los datos de end_size como tamaño de los datos, el archivo se desempaqueta correctamente. Aparentemente, al desempaquetar, el propio código determina el final de los datos, en lugar de confiar en la respuesta de esta función. Se podría dejar así, pero resulta que la función es inútil, lo cual es poco probable. Y quizás algún otro archivo falle...
Un experimento más demostró que si comentas las líneas

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

El archivo también empieza a descomprimirse. La cantidad de datos es cercana al 100%.

CompressedSize:106638447 
Resulta que el archivo tiene uint cdheader = 0x504b0102; y esto es una parte de los datos comprimidos, no una etiqueta de su final.

¿Se ha equivocado con la etiqueta? Encontré tal etiqueta en la búsqueda de Internet. Tal vez debería ser procesado de alguna otra manera en lugar de cortar los datos por ella, he cortado 30MB.

Función de trabajo con este archivo: (archivo \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;
   //esta es la memoria intermedia de anillo basado en byte desplazamiento a la izquierda: x = x << 8
   for(; size < end_size; size++)
   {
      pattern = pattern << 8;
      uint nbyte = zip_array[offset+size];
      pattern = pattern | nbyte;
      //comprobar 2 bytes superiores
      if((pattern & mask)!=(0x504b << 16))
         continue;
      //si dos bytes superiores son iguales a 0x504b comprueba todas las firmas
      if(pattern == header)
         break;
    // if(pattern == cdheader)
    // break;
   }
   //No se han encontrado firmas. Formato incorrecto.
   if(size == end_size-1)
      return 0;
   //Tamaño de retorno - tamaño de la firma.
   return size-sizeof(ZIP_LOCAL_HEADER)+1;
}
Puedo enviarte el archivo comprimido en un mensaje privado si te interesa averiguarlo.
 

Otro error con otro archivo. Esta vez comentar las líneas ayudó

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

Es decir, el código uint header = 0x504b0304; también aparece en el contenido del archivo y es descomprimido con éxito por 7Zip, Windows y esta versión corregida de CZip.

Dado que ambas salidas del bucle están desactivadas, el bucle se ha vuelto superfluo, puede ser borrado y devuelto:

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

Obviamente hay algún defecto en esta función. Después de todo, la condición

   //No se han encontrado firmas. Formato incorrecto.
   if(size == end_size-1)
      return 0;

nunca se cumplirá, porque al salir del bucle cuando se alcanza el final de los datos, tamaño == tamaño_fin será tamaño == tamaño_fin, no 1 menos.

Como resultado, he acortado la función a una línea:

int CZip::FindZipFileSize(const uchar &zip_array[],int offset)
{
   /*uint patrón = 0;
 int tamaño =0;
 uint header = 0x504b0304;
 uint cdheader =0x504b0102;
 uint mask = 0xffffff0000;
 int end_size = ArraySize(zip_array)-offset;
 //esta es la memoria intermedia en anillo basada en el desplazamiento a la izquierda del byte: x = x << 8
 for(; size < end_size; size++)
 {
 patrón = patrón << 8;
 uint nbyte = zip_array[offset+tamaño];
 ¡patrón = patrón | nbyte;
 //comprueba los 2 bytes superiores
 if((patrón & máscara)!=(0x504b << 16))
 continue;
 //si dos bytes superiores son iguales a 0x504b comprueba todas las firmas
// if(pattern == header)
//break;
// if(pattern == cdheader)
//break;
 }
 //No se han encontrado firmas. Formato incorrecto.
 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;
}

El programa con esta versión de la función ya ha descargado 110 archivos por 12 Gg en total y ha desempaquetado todo con éxito.

Si alguien tiene problemas con el desempaquetado, puede probar esta versión de la función.

 
Descargado y descomprimido casi 300 archivos. Y los datos que contienen son cada vez mayores y han alcanzado el límite de tamaño.
El archivo debería tener 1.800 millones de elementos char, pero al descomprimirlo se corta a 1.500 millones. Se pierden algunos datos. Es extraño que esté cortado tan corto, los arrays pueden tener hasta 2147483647 elementos.
La función terminal
produce datos cortados.
CryptDecode(CRYPT_ARCH_ZIP, m_file_puck, key, file_array);

No hay nada que hacer al respecto...

Pensé que es posible decodificar en bloques pares de kb/mb - suministré 1024, 1024*1024 y 1024*1024*10 - no funcionó.

Tendré que guardar los archivos, luego descomprimirlos manualmente y luego procesarlos. Será inconveniente - sin automatización ((

¿Hay alguna manera de utilizar el archivador de Windows? Usando WinExec para descomprimir en un archivo, y luego leerlo línea por línea. De esa manera la automatización se mantendrá. Pero no para el mercado.
 
Forester #:
¿Hay alguna forma de utilizar el archivador de Windows? Utiliza WinExec para descomprimir en un archivo y luego léelo línea por línea.

Puedes, obviamente. ¿Cuál es el problema? ¿Quizá lo he entendido mal? Existen archivadores de consola UnRAR.exe, UnZip.exe, etc. desde hace mucho tiempo.

 
Forester #:

Otro error con otro archivo. Esta vez comentar las líneas ayudó

Es decir, el código uint header = 0x504b0304; también aparece en el contenido del archivo y es descomprimido con éxito por 7Zip, Windows y esta versión corregida de CZip.

Dado que ambas salidas del bucle están desactivadas, el bucle se ha vuelto superfluo, puede ser borrado y devuelto:

Obviamente hay algún defecto en esta función. Después de todo, la condición

nunca se cumplirá, porque al salir del bucle cuando se alcanza el final de los datos, tamaño == tamaño_fin será tamaño == tamaño_fin, no 1 menos.

Como resultado, he acortado la función a una línea:

El programa con esta versión de la función ya ha descargado 110 archivos por 12 Gg en total y ha desempaquetado todo con éxito.

Si alguien tiene problemas con el desempaquetado, puede probar esta versión de la función.

Puedo suponer que todavía hay que buscar etiquetas, pero no en el cuerpo del archivo, sino entre los archivos, si hay varios. Probablemente las longitudes de archivo deben ser registrados en alguna parte....
En general, mi solución es privada para mi tarea con 1 archivo en el archivo, si hay varios de ellos - tal vez usted necesita hacer algo más.

 
Forester #:

Puedo suponer que todavía hay que buscar las etiquetas, pero no en el cuerpo del archivo, sino entre los archivos, si hay varios. Probablemente las longitudes del archivo debe ser registrado en algún lugar....
En general, mi solución es privada para mi tarea con 1 archivo en el archivo, si hay varios de ellos - tal vez es necesario hacer algo más.

Hice una impresión de los tamaños comprimidos y sin comprimir que deben estar en las cabeceras.
Los archivos que he descargado - tienen 0 0. Si size=0, entonces se llama a FindZipFileSize() discutido anteriormente.

He creado un archivo con el 1er archivo usando un archivador normal. Los tamaños en la cabecera:
46389587 376516461

Y otro archivo con 2 ficheros más, incluyendo el añadido al primer archivo:
46981880 314725045
46389587 376516461

Ambos tienen los tamaños escritos en las cabeceras y no llamaron a FindZipFileSize()

Y los archivos que descargué (con 0 0 en tamaños) aparentemente fueron creados por un software que no escribió los tamaños en las cabeceras.

Quizás mi solución para acortar FindZipFileSize() sea universal.

 
Edgar Akhmadeev #:

Podemos, obviamente. ¿Cuál es el problema? ¿Quizá lo he entendido mal? Hace mucho tiempo que existen los archivadores de consola UnRAR.exe, UnZip.exe, etc.

Hice desempaquetar a través de 7-zip (UnZip.exe no se ha actualizado desde 2009 sobre el apoyo incluso Win 7 no está escrito).
He comparado la velocidad:
Esta biblioteca CZIP:

2025.06.14 20:59:06.758 Archivo abierto con éxito. Total de archivos: 1.
2025.06.14 20:59:07.34 5 UnZipped
587ms

7-zip:
2025.06.14 21:00:07.312 Inicio descompresión por 7-Zip.
2025.06.14 21:00:09.274 Descomprimido por 7-Zip. Tamaño del archivo: 428.22 MB
1962 ms

Esto es sólo para descomprimir el archivo con reinicio a disco SSD. También hay que leer el archivo desde el disco línea por línea.

Tiempo total desde el inicio hasta el final del análisis:
Tiempo total: 10s 709ms
y
Tiempo total: 12s 892ms
La diferencia es de 2s 183 ms.

En general, es preferible usar esta librería CZIP por velocidad y si el archivo es demasiado grande, usar otros archivadores.

Para mi para ~1000 archivos a 2secs cada uno = 33 minutos de ahorro. En realidad más, ya que este es un ejemplo con el archivo más pequeño a 428 mb, CZIP descomprime archivos hasta ~1.7 GB.
Cualquier archivo mayor (hasta 4 GB) lo descomprime 7-zip. Así que hay 1-1,5 horas de ahorro.

Probé CZIP con mis ediciones - descomprimí más de 600 archivos en 100GB sin errores.