Tengo un archivo zip con un tamaño de 1,5 GB.
Su contenido es un gran archivo de texto plano ridículo (60 GB) y actualmente no tengo suficiente espacio en mi disco para extraerlo ni quiero extraerlo todo, incluso si lo tuviera.
En cuanto a mi caso de uso, sería suficiente si puedo inspeccionar partes del contenido.
Por lo tanto, quiero descomprimir el archivo como una secuencia y acceder a un rango del archivo (como uno puede a través de la cabeza y la cola en un archivo de texto normal).
Ya sea por memoria (por ejemplo, extraer un máximo de 100 kb a partir de la marca de 32 GB) o por líneas (deme las líneas de texto sin formato 3700-3900).
¿Hay alguna manera de lograr eso?
text-processing
zip
k0pernikus
fuente
fuente
Respuestas:
Tenga en cuenta que
gzip
puede extraerzip
archivos (al menos la primera entrada en elzip
archivo). Entonces, si solo hay un archivo enorme en ese archivo, puede hacer:Para extraer las 20 líneas comenzando con la 3000 por ejemplo.
O:
Por lo mismo con bytes (suponiendo una
head
implementación que soporte-c
).Para cualquier miembro arbitrario en el archivo, de manera Unixy:
Con la
head
construcción deksh93
(como cuando/opt/ast/bin
está adelante$PATH
), también puede hacer:Tenga en cuenta que, en cualquier caso,
gzip
/bsdtar
/unzip
siempre tendrá que descomprimir (y descartar aquí) toda la sección del archivo que conduce a la parte que desea extraer. Eso se debe a cómo funciona el algoritmo de compresión.fuente
gzip
puede manejarlo, lo hará el resto "Z" conscientes utilidades (zcat
,zless
, etc.) También trabajo?gzip
(generalmente ciertozless
, no necesariamente dezcat
qué en algunos sistemas solo se deben leer.Z
archivos), sí.Una solución que utiliza descomprimir -p y dd, por ejemplo, para extraer 10 kb con 1000 bloques de desplazamiento:
Nota: No probé esto con datos realmente enormes ...
fuente
unzip -l ARCHIVE
para enumerar el contenido del archivo yunzip -p ARCHIVE PATH
extraer el contenido de un solo objetoPATH
para stdout.dd
en tuberías con conteo u omisión no es confiable, ya que hará muchosread()
s de hasta 1024 bytes. Por lo tanto, solo se garantiza que funcione correctamente siunzip
escribe en la tubería en trozos cuyo tamaño es un múltiplo de 1024.Si tiene control sobre la creación de ese gran archivo zip, ¿por qué no considerar usar una combinación de
gzip
yzless
?Esto le permitiría usarlo
zless
como un buscapersonas y ver el contenido del archivo sin tener que molestarse con la extracción.Si no puede cambiar el formato de compresión, obviamente esto no funcionaría. Si es así, siento que
zless
es bastante conveniente.fuente
Para ver líneas específicas del archivo, canalice la salida al editor de flujo de Unix, sed . Esto puede procesar flujos de datos arbitrariamente grandes, por lo que incluso puede usarlos para cambiar los datos. Para ver las líneas 3700-3900 como lo solicitó, ejecute lo siguiente.
fuente
sed -n 3700,3900p
seguirá leyendo hasta el final del archivo. Es mejor usarsed '3700,$!d;3900q'
para evitar eso, o incluso en general más eficiente:tail -n +3700 | head -n 201
Me preguntaba si era posible hacer algo más eficiente que descomprimir desde el inicio del archivo hasta el punto. Parece que la respuesta es no. Sin embargo, en algunas CPU (Skylake)
zcat | tail
no sube la CPU a la velocidad de reloj completa. Vea abajo. Un decodificador personalizado podría evitar ese problema y guardar las llamadas del sistema de escritura de tubería, y tal vez ser ~ 10% más rápido. (O ~ 60% más rápido en Skylake si no modifica la configuración de administración de energía).Lo mejor que podría hacer con un zlib personalizado con una
skipbytes
función sería analizar los símbolos en un bloque de compresión para llegar al final sin hacer el trabajo de reconstruir realmente el bloque descomprimido. Esto podría ser significativamente más rápido (probablemente al menos 2 veces) que llamar a la función de decodificación regular de zlib para sobrescribir el mismo búfer y avanzar en el archivo. Pero no sé si alguien ha escrito tal función. (Y creo que esto realmente no funciona a menos que el archivo se haya escrito especialmente para permitir que el decodificador se reinicie en un determinado bloque).Tenía la esperanza de que hubiera una manera de saltar los bloques Deflate sin decodificarlos, porque eso sería mucho más rápido. El árbol Huffman se envía al comienzo de cada bloque, por lo que puede decodificar desde el comienzo de cualquier bloque (creo). Oh, creo que el estado del decodificador es más que el árbol Huffman, también es el 32kB anterior de datos decodificados, y esto no se restablece / olvida a través de los límites de bloque de forma predeterminada. Los mismos bytes pueden seguir siendo referenciados repetidamente, por lo que solo pueden aparecer literalmente una vez en un archivo comprimido gigante. (por ejemplo, en un archivo de registro, el nombre de host probablemente permanece "activo" en el diccionario de compresión todo el tiempo, y cada instancia hace referencia al anterior, no al primero).
El
zlib
manual dice que debe usarZ_FULL_FLUSH
al llamardeflate
si desea que la secuencia comprimida pueda buscarse en ese punto. "Restablece el estado de compresión", por lo que creo que sin eso, las referencias hacia atrás pueden ir a los bloques anteriores. Entonces, a menos que su archivo zip se haya escrito con bloques de descarga completa ocasionales (como cada 1G o algo tendría un impacto insignificante en la compresión), creo que tendría que hacer más trabajo de decodificación hasta el punto que deseaba de lo que inicialmente estaba pensando. Supongo que probablemente no puedas comenzar al comienzo de cualquier bloque.El resto de esto se escribió mientras pensaba que sería posible encontrar el comienzo del bloque que contiene el primer byte que desea y decodificar desde allí.
Pero desafortunadamente, el inicio de un bloque Deflate no indica cuánto tiempo es para bloques comprimidos. Los datos incompatibles se pueden codificar con un tipo de bloque sin comprimir que tiene un tamaño de 16 bits en bytes en la parte frontal, pero los bloques comprimidos no: RFC 1951 describe el formato de forma bastante legible . Los bloques con codificación dinámica de Huffman tienen el árbol al frente del bloque (para que el descompresor no tenga que buscar en la secuencia), por lo que el compresor debe haber mantenido todo el bloque (comprimido) en la memoria antes de escribirlo.
La distancia máxima de referencia hacia atrás es de solo 32 kB, por lo que el compresor no necesita mantener muchos datos sin comprimir en la memoria, pero eso no limita el tamaño del bloque. Los bloques pueden tener varios megabytes de longitud. (Esto es lo suficientemente grande como para que el disco valga la pena incluso en una unidad magnética, frente a la lectura secuencial en la memoria y simplemente omitir datos en la RAM, si fuera posible encontrar el final del bloque actual sin analizarlo).
zlib crea bloques el mayor tiempo posible: según Marc Adler , zlib solo comienza un nuevo bloque cuando el búfer de símbolos se llena, lo que con la configuración predeterminada es 16.383 símbolos (literales o coincidencias)
Comprimí la salida de
seq
(que es extremadamente redundante y, por lo tanto, probablemente no sea una gran prueba), peropv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
funciona con solo ~ 62 MiB / s de datos comprimidos en un Skylake i7-6700k a 3.9GHz, con DDR4-2666 RAM. Eso es 246MiB / s de datos descomprimidos, que es un cambio considerable en comparación con lamemcpy
velocidad de ~ 12 GiB / s para tamaños de bloque demasiado grandes para caber en la memoria caché.(Con el
energy_performance_preference
ajuste predeterminado enbalance_power
lugar debalance_performance
, el gobernador interno de la CPU de Skylake decide ejecutar solo a 2.7GHz, ~ 43 MiB / s de datos comprimidos. Lo usosudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
para ajustarlo. Probablemente estas llamadas frecuentes del sistema no parecen estar realmente vinculadas a la CPU trabajar para la unidad de administración de energía.)TL: DR:
zcat | tail -c
está vinculado a la CPU incluso en una CPU rápida, a menos que tenga discos muy lentos. gzip usó el 100% de la CPU en la que se ejecutaba (y ejecutó 1.81 instrucciones por reloj, segúnperf
), ytail
utilizó 0.162 de la CPU en la que se ejecutó (0.58 IPC). El sistema estaba en su mayor parte inactivo.Estoy usando Linux 4.14.11-1-ARCH, que tiene KPTI habilitado de manera predeterminada para evitar Meltdown, por lo que todas esas
write
llamadas al sistemagzip
son más caras de lo que solían ser: /Tener la búsqueda incorporada en
unzip
ozcat
(pero aún utilizando lazlib
función de decodificación regular ) ahorraría todas esas escrituras de tubería, y conseguiría que las CPU Skylake funcionen a la velocidad de reloj completa. (Este downclocking para algunos tipos de carga es exclusivo de Intel Skylake y posterior, que han descargado la toma de decisiones de frecuencia de la CPU del sistema operativo, porque tienen más datos sobre lo que está haciendo la CPU y pueden aumentar / disminuir más rápido. Esto es normalmente bueno, pero aquí lleva a Skylake a no acelerar a toda velocidad con una configuración de gobernador más conservadora).No hay llamadas del sistema, solo reescribir un búfer que se ajuste en la caché L2 hasta que llegue a la posición de byte inicial que desea, probablemente supondría al menos un% de diferencia. Tal vez incluso el 10%, pero solo estoy inventando números aquí. No he perfilado
zlib
ningún detalle para ver qué tan grande es la huella de caché, y cuánto duele el vaciado TLB (y, por lo tanto, el vaciado uop-cache) en cada llamada al sistema con KPTI habilitado.Hay algunos proyectos de software que agregan un índice de búsqueda al formato de archivo gzip . Esto no le ayuda si no puede lograr que nadie genere archivos comprimidos buscables para usted, pero otros futuros lectores pueden beneficiarse.
Es de suponer que ninguno de estos proyectos tienen una función de decodificación que sabe cómo saltar a través de una corriente desinflarse sin un índice, ya que sólo están diseñados para trabajar cuando un índice está disponible.
fuente
Puede abrir el archivo zip en una sesión de Python, utilizando
zf = zipfile.ZipFile(filename, 'r', allowZip64=True)
y una vez abierto puede abrir, para leer, cualquier archivo dentro del archivo zip y leer líneas, etc., como si fuera un archivo normal.fuente