¿Por qué Zip puede comprimir un solo archivo más pequeño que varios archivos con el mismo contenido?

126

Supongamos que tengo 10,000 archivos XML. Ahora supongamos que quiero enviárselos a un amigo. Antes de enviarlos, me gustaría comprimirlos.

Método 1: no comprimirlos

Resultados:

Resulting Size: 62 MB
Percent of initial size: 100%

Método 2: comprime cada archivo y envíale 10,000 archivos xml

Mando:

for x in $(ls -1) ;  do   echo $x ; zip "$x.zip" $x ; done

Resultados:

Resulting Size: 13 MB
Percent of initial size: 20%

Método 3: crear un único zip que contenga 10,000 archivos xml

Mando:

zip all.zip $(ls -1)

Resultados:

Resulting Size: 12 MB
Percent of initial size: 19%

Método 4: Concatenar los archivos en un solo archivo y comprimirlo

Mando:

cat *.xml > oneFile.txt ; zip oneFile.zip oneFile.txt

Resultados:

Resulting Size: 2 MB
Percent of initial size: 3%

Preguntas:

  • ¿Por qué obtengo resultados tan dramáticamente mejores cuando solo estoy comprimiendo un solo archivo?
  • Esperaba obtener resultados drásticamente mejores con el método 3 que con el método 2, pero no lo hago. ¿Por qué?
  • ¿Es específico este comportamiento zip? Si lo intentara gzip, ¿obtendría resultados diferentes?

Información adicional:

$ zip --version
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
This is Zip 3.0 (July 5th 2008), by Info-ZIP.
Currently maintained by E. Gordon.  Please send bug reports to
the authors using the web page at www.info-zip.org; see README for details.

Latest sources and executables are at ftp://ftp.info-zip.org/pub/infozip,
as of above date; see http://www.info-zip.org/ for other sites.

Compiled with gcc 4.4.4 20100525 (Red Hat 4.4.4-5) for Unix (Linux ELF) on Nov 11 2010.

Zip special compilation options:
    USE_EF_UT_TIME       (store Universal Time)
    SYMLINK_SUPPORT      (symbolic links supported)
    LARGE_FILE_SUPPORT   (can read and write large files on file system)
    ZIP64_SUPPORT        (use Zip64 to store large files in archives)
    UNICODE_SUPPORT      (store and read UTF-8 Unicode paths)
    STORE_UNIX_UIDs_GIDs (store UID/GID sizes/values using new extra field)
    UIDGID_NOT_16BIT     (old Unix 16-bit UID/GID extra field not used)
    [encryption, version 2.91 of 05 Jan 2007] (modified for Zip 3)

Editar: metadatos

Una respuesta sugiere que la diferencia son los metadatos del sistema que se almacenan en el archivo zip. No creo que este pueda ser el caso. Para probar, hice lo siguiente:

for x in $(seq 10000) ; do touch $x ; done
zip allZip $(ls -1)

El zip resultante es 1.4MB. Esto significa que todavía hay ~ 10 MB de espacio inexplicable.

Sixtyfootersdude
fuente
34
Si no me equivoco, es esta fenomona la que hace que las personas creen .tar.gzen lugar de simplemente comprimir todo el directorio.
corsiKa
18
Una pregunta similar ya se le pidió, tl; dr utilizar archivos 7zip sólidos.
Dmitry Grigoryev
3
@sixtyfootersdude Como prueba para validar algunas de las respuestas, ¿puedes intentar comprimir el zip producido en el método 3? Sospecho que esto reducirá el tamaño del archivo a algo comparable al método 4.
Travis
77
En lugar de $(ls -1), simplemente use *: for x in *; zip all.zip *
muru
44
Si desea hacer una compresión sólida con ZIP, aquí hay una solución alternativa: primero, cree un ZIP sin comprimir que contenga todos sus archivos. Luego, coloca ese ZIP dentro de otro ZIP comprimido.
usuario20574

Respuestas:

129

Zip trata el contenido de cada archivo por separado cuando se comprime. Cada archivo tendrá su propia secuencia comprimida. Hay soporte dentro del algoritmo de compresión (típicamente DEFLATE ) para identificar secciones repetidas. Sin embargo, no hay soporte en Zip para encontrar redundancia entre archivos.

Es por eso que hay tanto espacio extra cuando el contenido está en múltiples archivos: está poniendo la misma secuencia comprimida en el archivo varias veces.

Alan Shutko
fuente
99
También es la razón por la cual algunas herramientas de compresión le dan la opción de comprimir los archivos por separado o como una sola entidad. (Aunque generalmente eso también significa que tiene que descomprimir más del archivo de lo que lo haría si quisiera ver un solo archivo en él.)
JAB
28
@JAB: Las herramientas de compresión como 7z y rar usan el término archivo "sólido" para empaquetar varios archivos cabeza a cola en flujos de compresión más grandes. Con un tamaño de fragmento moderado como 64MiB, el acceso aleatorio a un solo archivo puede requerir descomprimir hasta 64MiB de datos desde el inicio del bloque de compresión en el que se encuentra. Puede obtener una compensación decente entre el acceso aleatorio y la búsqueda de redundancia entre archivos. 7z puede usar el esquema de compresión LZMA más efectivo (pero más lento para comprimir), que es otra ventaja sobre zip.
Peter Cordes
¿Estás diciendo que there is no support in Zip to find redundancy between filesestá en la especificación del archivo zip?
sixtyfootersdude
66
@sixtyfootersdude Muchos algoritmos de compresión, como DEFLATE, funcionan como una secuencia. Para recuperar suficiente información para descomprimir una parte de la secuencia, debe procesar la secuencia completa hasta ese punto. Si intentaron encontrar la redundancia entre archivos, tendría que descomprimir los 1000 archivos para llegar al último. Por lo general, así es como funciona tgz. Sin embargo, zip fue diseñado para permitirle extraer archivos individuales. tgz está diseñado para ser más todo o nada
Cort Ammon
1
@sixtyfootersdude, eso es correcto. Parafraseando a Cort: las especificaciones de pkzip no admiten el trabajo cruzado de archivos. Si lo hicieran, extraer un archivo podría requerir la extracción de todo el archivo (y todos los archivos).
James Snell
48

La compresión ZIP se basa en patrones repetitivos en los datos a comprimir, y la compresión mejora a medida que el archivo es más largo, ya que se pueden encontrar y usar más patrones más largos.

Simplificado, si comprime un archivo, el diccionario que asigna códigos (cortos) a patrones (más largos) está necesariamente contenido en cada archivo zip resultante; Si comprime un archivo largo, el diccionario se 'reutiliza' y se vuelve aún más efectivo en todo el contenido.

Si sus archivos son incluso un poco similares (como siempre lo es el texto), la reutilización del 'diccionario' se vuelve muy eficiente, y el resultado es un zip total mucho más pequeño.

Aganju
fuente
3
ZIP hace tanto el archivo como la compresión. ¿Significa esto que ZIP comprime cada archivo individualmente, incluso si todos terminan en el mismo archivo ZIP?
gerrit
2
tiene que hacerlo: imagina que eliminas un solo archivo, no querrás que pase otra media hora volviendo a comprimir el resto con un nuevo 'diccionario'. - también, probablemente asume que diferentes archivos necesitan 'diccionarios' muy diferentes.
Aganju
2
No veo por qué tiene que hacerlo. Con las herramientas de Unix, primero archivaría un archivo con tar, luego lo comprimiría con gzip / bz2 / lzma. Al algoritmo de compresión no le importa cuántos archivos están codificados en el archivo. Además, ¿qué tan común es realmente eliminar un solo archivo de un archivo comprimido? No creo haberlo hecho nunca.
gerrit
44
No estoy en desacuerdo, y esa es probablemente una buena manera. No diseñé ni escribí ZIP. Acabo de decir lo que hace ...
Aganju
16
@gerrit Tiene sus propios problemas. Zip está diseñado para permitirle acceder rápidamente a cualquier archivo en el archivo: intente desempacar un solo archivo de un archivo de 100 GiB UHA y verá por qué lo eligieron de esta manera. También está diseñado para agregar: puede tener su zip de copia de seguridad y seguir agregando (o reemplazando) archivos según sea necesario. Todo esto es de gran ayuda cuando se usan archivos. La desventaja es que si está comprimiendo archivos que son muy similares (que no es tan común), no puede explotar las similitudes para reducir el tamaño del archivo.
Luaan
43

En Zip, cada archivo se comprime por separado. Lo contrario es 'compresión sólida', es decir, los archivos se comprimen juntos. 7-zip y Rar usan compresión sólida por defecto. Gzip y Bzip2 no pueden comprimir varios archivos, por lo que Tar se usa primero, teniendo el mismo efecto que la compresión sólida.

Como el archivo xml tiene una estructura similar y probablemente un contenido similar, si los archivos se comprimen juntos, la compresión será mayor.

Por ejemplo, si un archivo contiene la cadena "<content><element name="y el compresor ya encontró esa cadena en otro archivo, lo reemplazará con un pequeño puntero a la coincidencia anterior, si el compresor no usa 'compresión sólida' la primera vez que la cadena aparece en el el archivo se grabará como un literal que es más grande.

ggf31416
fuente
9

Zip no solo almacena el contenido del archivo, sino que también almacena los metadatos del archivo, como la identificación del usuario propietario, los permisos, los tiempos de creación y modificación, etc. Si tiene un archivo, tiene un conjunto de metadatos; si tiene 10,000 archivos, tiene 10,000 conjuntos de metadatos.

Mike Scott
fuente
3
Buen punto, pero los metadatos del sistema solo ocupan 1.4MB de espacio. Mira mi edición.
sixtyfootersdude
1
No estoy familiarizado con el algoritmo zip, pero los metadatos no son solo la información del archivo, sino también cosas como el tamaño y un diccionario, posiblemente alguna información sobre la distribución de caracteres. Un diccionario en un archivo de texto no vacío no será cero. Probablemente por eso ves que los metadatos son más grandes en tus archivos xml que en tus archivos vacíos.
Ben Richards
Este fue mi primer pensamiento. Información del encabezado del archivo
zip
Esto solo explica la diferencia entre 2 y 3, no 4.
Luaan
@Luaan No, en 2 y 3 los metadatos para los 10,000 archivos se incluyen en el archivo o archivos zip, por lo que el tamaño total del archivo es casi del mismo tamaño. En 4, solo hay metadatos para un archivo, y el archivo zip es mucho más pequeño.
Mike Scott
7

Una opción perdida por el OP es comprimir todos los archivos junto con la compresión desactivada, luego comprimir el zip resultante con la compresión establecida al máximo. Esto emula aproximadamente el comportamiento de los archivos comprimidos * nix .tar.Z, .tar.gz, .tar.bz, etc., al permitir que la compresión explote las redundancias a través de los límites de los archivos (que el algoritmo ZIP no puede hacer cuando se ejecuta en un solo archivo) pasar). Esto permite que los archivos XML individuales se extraigan más tarde, pero maximiza la compresión. La desventaja es que el proceso de extracción requiere un paso adicional, utilizando temporalmente mucho más espacio en disco del que se necesitaría para un .zip normal.

Con la ubicuidad de herramientas gratuitas como 7-Zip para extender la familia tar a Windows, realmente no hay razón para no usar un .tar.gz o .tar.bz, etc., ya que Linux, OS X y los BSD tienen todos herramientas nativas para manipularlos.

Monty Harder
fuente
gzip y bzip2 podrían terminar peor porque están diseñados teniendo en cuenta las secuencias de compresión, por lo que tendrán que comenzar a generar datos comprimidos antes de que se conozcan todos los datos a comprimir.
rackandboneman
@rackandboneman: esta es la compensación que debe realizar al comprimir archivos de mayor tamaño que la cantidad de memoria que está dispuesto a usar en el momento de la compresión. (Y también, la cantidad de tiempo de CPU requerida para encontrar algo globalmente óptimo sería enorme). Un gran diccionario de compresión también puede aumentar la memoria requerida para la descompresión . Esta es una opción para LZMA ( xz/ 7-zip). De todos modos, los diccionarios adaptativos pueden detectar patrones una vez que son visibles. No es que solo construya un sistema de codificación estático basado en los primeros 32k. Es por eso que gzip no apesta.
Peter Cordes
Realmente me gusta este "truco" si necesita quedarse con el formato zip. No estoy de acuerdo con su "no hay razón para no usar 7-zip"; si envío un archivo a un amigo no técnico, quiero asegurarme de que pueda abrirlo fácilmente. Si estoy enviando a un cliente comercial, aún más.
Wowfunhappy
5

El formato de compresión zip almacena y comprime cada archivo por separado. No aprovecha la repetición entre archivos, solo dentro de un archivo.

La concatenación del archivo permite que zip aproveche las repeticiones en todos los archivos, lo que resulta en una compresión drásticamente mayor.

Por ejemplo, supongamos que cada archivo XML tiene un encabezado determinado. Ese encabezado solo aparece una vez en cada archivo, pero se repite casi de manera idéntica en muchos otros archivos. En los métodos 2 y 3, zip no pudo comprimir esto, pero en el método 4 sí pudo.

BonsaiOak
fuente
3
¿Cómo es esto diferente de una de las 3 mejores respuestas ya publicadas 5 horas antes?
Xen2050
1
@ Xen2050 No hay mucha diferencia, solo pensé que podría explicarlo más claramente.
BonsaiOak
1
@BonsaiOak: luego agregue un comentario a la respuesta correcta o edite si tiene suficiente representante. De lo contrario, pero su comentario agrega claridad, alguien más podría recoger esto y editar la publicación de todos modos.
AdamV
@ AdamV entiendo tu punto. Mi respuesta actualmente no agrega ninguna información útil, aunque podría decirse que sí cuando la escribí. Ya hay comentarios apropiados debajo de la primera respuesta, así que tampoco veo el punto de agregarlos. ¿Estás diciendo que debería cerrar mi respuesta? ¿Qué daño hay al dejarlo abierto?
BonsaiOak
4

Junto a los metadatos que mencionó Mike Scott, también hay una sobrecarga en el algoritmo de compresión.

Al comprimir un montón de archivos pequeños individuales, tendrá que ser muy afortunado para poder comprimirlos, ya que simplemente llena un bloque de compresión. Al comprimir un solo bloque monolítico, el sistema puede continuar transmitiendo datos a su algoritmo, ignorando los 'límites' (por falta de una mejor palabra) de los archivos individuales.

También se sabe que ASCII tiene un alto factor de compresión. Además, xml suele ser muy repetitivo, lo que convierte a los metadatos en una gran parte de los datos que no se pueden comprimir tan fácilmente como el contenido xml.

Por último, si la memoria funciona correctamente, zip usa algo como la codificación de diccionario, que es especialmente eficaz en archivos ASCII y aún más en XML debido a su repetitividad

Compresión de datos explicada: http://mattmahoney.net/dc/dce.html

GapWim
fuente
3

Considere este XML:

<root>
  <element id="1" />
  <element id="2" /> 
  <other id="3" />
  ...
</root>

Un XML tiene una estructura muy repetitiva, Zip aprovecha esas repeticiones para construir un diccionario cuyo patrón tiene más ocurrencias y luego, al comprimir, usa menos bits para almacenar más patrones repetidos y más bits para almacenar patrones menos repetidos .

Cuando concatena esos archivos, el archivo fuente (la fuente para zip) es grande pero contiene patrones mucho más repetidos porque la distribución de las estructuras aburridas de un XML se amortiza en el archivo completo grande, dando la posibilidad de que ZIP almacene esos patrones. usando menos pedazos.

Ahora, si combina diferentes XML en un solo archivo, incluso cuando esos archivos tienen nombres de etiquetas completamente diferentes, el algoritmo de compresión encontrará la mejor distribución de patrones en todos los archivos y no archivo por archivo.

Finalmente, el algoritmo de compresión ha encontrado la mejor distribución de patrones repetidos.

rnrneverdies
fuente
-1

Además de la respuesta 7-Zip, hay otro enfoque que no es tan bueno pero que valdría la pena probar si por alguna razón no desea usar 7-Zip:

Comprime el archivo zip. Ahora, normalmente un archivo zip es incompresible, pero cuando contiene muchos archivos idénticos, el compresor puede encontrar esta redundancia y comprimirla. Tenga en cuenta que también he visto una pequeña ganancia al tratar con grandes cantidades de archivos sin redundancia. Si realmente te importa el tamaño, vale la pena intentarlo si tienes una gran cantidad de archivos en tu zip.

Loren Pechtel
fuente
Eso solo funciona si haces el primer zip con la compresión desactivada como mencioné anteriormente.
Monty Harder
@MontyHarder Lo he visto funcionar con la compresión activada.
Loren Pechtel