Eu tenho um arquivo zip com tamanho de 1,5 GB.
Seu conteúdo é um arquivo de texto simples grande e ridículo (60 GB) e, atualmente, não tenho espaço suficiente no disco para extrair tudo, nem quero extrair tudo, mesmo que tivesse.
Quanto ao meu caso de uso, seria suficiente se eu pudesse inspecionar partes do conteúdo.
Por isso, quero descompactar o arquivo como um fluxo e acessar um intervalo do arquivo (como se pode via cabeça e cauda em um arquivo de texto normal).
Pela memória (por exemplo, extraia no máximo 100kb a partir da marca de 32GB) ou por linhas (dê-me as linhas de texto sem formatação 3700-3900).
Existe uma maneira de conseguir isso?
text-processing
zip
k0pernikus
fonte
fonte
Respostas:
Observe que
gzip
pode extrairzip
arquivos (pelo menos a primeira entrada nozip
arquivo). Portanto, se houver apenas um arquivo enorme nesse arquivo, você poderá:Para extrair as 20 linhas começando pela 3000, por exemplo.
Ou:
Para a mesma coisa com bytes (assumindo uma
head
implementação que suporta-c
).Para qualquer membro arbitrário do arquivo, de maneira Unixy:
Com o
head
built-in deksh93
(como quando/opt/ast/bin
está à frente$PATH
), você também pode:Observe que, em qualquer caso,
gzip
/bsdtar
/unzip
sempre precisará descompactar (e descartar aqui) toda a seção do arquivo que leva à parte que você deseja extrair. Isso é como o algoritmo de compactação funciona.fonte
gzip
pode lidar com isso, será que as outras "z conscientes" utilities (zcat
,zless
etc.) também trabalho?gzip
(geralmente verdadezless
, não necessariamente dozcat
que em alguns sistemas ainda é.Z
apenas para ler arquivos), sim.Uma solução usando descompacte -p e dd, por exemplo, para extrair 10kb com deslocamento de 1000 blocos:
Nota: não tentei isso com dados realmente enormes ...
fonte
unzip -l ARCHIVE
para listar o conteúdo do archive eunzip -p ARCHIVE PATH
extrair o conteúdo de um único objetoPATH
para o stdout.dd
em pipes com count ou skip não é confiável, pois fará muitosread()
s de até 1024 bytes. Por isso, só é garantido para trabalho corretamente seunzip
escreve para o tubo em pedaços, cujo tamanho é um múltiplo de 1024.Se você tem controle sobre a criação desse grande arquivo zip, por que não considerar usar uma combinação de
gzip
ezless
?Isso permitiria que você usasse
zless
como pager e visualizasse o conteúdo do arquivo sem precisar se preocupar com a extração.Se você não pode alterar o formato de compactação, isso obviamente não funcionaria. Se assim for, eu sinto que
zless
é bastante conveniente.fonte
Para visualizar linhas específicas do arquivo, canalize a saída para o editor de fluxo Unix, sed . Isso pode processar fluxos de dados arbitrariamente grandes, para que você possa usá-lo para alterar os dados. Para visualizar as linhas 3700-3900 conforme solicitado, execute o seguinte.
fonte
sed -n 3700,3900p
continuará lendo até o final do arquivo. É melhor usarsed '3700,$!d;3900q'
para evitar isso, ou mesmo geralmente mais eficiente:tail -n +3700 | head -n 201
Gostaria de saber se era possível fazer algo mais eficiente do que descomprimir desde o início do arquivo até o ponto. Parece que a resposta é não. No entanto, em algumas CPUs (Skylake)
zcat | tail
não aumenta a CPU até a velocidade máxima do relógio. Ver abaixo. Um decodificador personalizado pode evitar esse problema e salvar as chamadas do sistema de gravação de canal e talvez ser ~ 10% mais rápido. (Ou ~ 60% mais rápido no Skylake, se você não ajustar as configurações de gerenciamento de energia).O melhor que você poderia fazer com um zlib personalizado com uma
skipbytes
função seria analisar os símbolos em um bloco de compactação para chegar ao fim sem fazer o trabalho de reconstruir o bloco descompactado. Isso pode ser significativamente mais rápido (provavelmente pelo menos 2x) do que chamar a função de decodificação regular do zlib para substituir o mesmo buffer e avançar no arquivo. Mas não sei se alguém escreveu essa função. (E acho que isso realmente não funciona, a menos que o arquivo tenha sido escrito especialmente para permitir que o decodificador reinicie em um determinado bloco).Eu esperava que houvesse uma maneira de pular os blocos Deflate sem decodificá-los, porque isso seria muito mais rápido. A árvore Huffman é enviada no início de cada bloco, para que você possa decodificar desde o início de qualquer bloco (eu acho). Ah, acho que o estado do decodificador é mais do que a árvore de Huffman, também são os 32 kiB anteriores de dados decodificados, e isso não é redefinido / esquecido nos limites do bloco por padrão. Os mesmos bytes podem continuar sendo referenciados repetidamente, portanto, podem aparecer literalmente apenas uma vez em um arquivo compactado gigante. (por exemplo, em um arquivo de log, o nome do host provavelmente permanece "quente" no dicionário de compactação o tempo todo, e todas as instâncias referenciam o anterior, não o primeiro).
O
zlib
manual diz que você deve usarZ_FULL_FLUSH
ao ligardeflate
se quiser que o fluxo compactado seja procurado até esse ponto. Ele "redefine o estado de compactação", então acho que sem isso, as referências anteriores podem ir para o (s) bloco (s) anterior (es). Portanto, a menos que seu arquivo zip tenha sido gravado com blocos ocasionais de liberação completa (como todo 1G ou algo teria um impacto insignificante na compactação), acho que você teria que fazer mais do trabalho de decodificação até o ponto desejado do que eu estava inicialmente pensando. Eu acho que você provavelmente não pode começar no início de qualquer bloco.O resto disso foi escrito enquanto eu pensava que seria possível encontrar o início do bloco que contém o primeiro byte desejado e decodificar a partir daí.
Infelizmente, porém, o início de um bloco Deflate não indica quanto tempo é , para blocos compactados. Os dados incompatíveis podem ser codificados com um tipo de bloco não compactado que possui um tamanho de 16 bits em bytes na frente, mas os blocos compactados não: O RFC 1951 descreve o formato com bastante facilidade . Blocos com codificação dinâmica de Huffman têm a árvore na frente do bloco (para que o descompressor não precise procurar no fluxo), portanto o compressor deve manter todo o bloco (comprimido) na memória antes de escrevê-lo.
A distância máxima de referência para trás é de apenas 32 kB, portanto o compressor não precisa manter muitos dados não compactados na memória, mas isso não limita o tamanho do bloco. Os blocos podem ter vários megabytes. (Isso é grande o suficiente para que o disco busque valer a pena, mesmo em uma unidade magnética, vs. leitura seqüencial na memória e apenas pular dados na RAM, se for possível encontrar o final do bloco atual sem analisá-lo).
O zlib cria blocos o maior tempo possível: De acordo com Marc Adler , o zlib só inicia um novo bloco quando o buffer do símbolo é preenchido, que com a configuração padrão é 16.383 símbolos (literais ou correspondências)
Gzipei a saída de
seq
(que é extremamente redundante e, portanto, provavelmente não é um ótimo teste), maspv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
é executado com apenas 62 MiB / s de dados compactados em um Skylake i7-6700k a 3.9GHz, com DDR4-2666 RAM. São 246MiB / s de dados descomprimidos, que são trocas de chump em comparação com amemcpy
velocidade de ~ 12 GiB / s para tamanhos de bloco grandes demais para caber no cache.(Com
energy_performance_preference
o padrão definido embalance_power
vez debalance_performance
, o governador interno da CPU do Skylake decide rodar apenas a 2,7 GHz, ~ 43 MiB / s de dados compactados. Eu usosudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
para ajustá-los. Provavelmente, essas chamadas frequentes do sistema não parecem reais vinculadas à CPU trabalho para a unidade de gerenciamento de energia.)TL: DR:
zcat | tail -c
é vinculado à CPU mesmo em uma CPU rápida, a menos que você tenha discos muito lentos. O gzip usou 100% da CPU na qual executou (e executou 1,81 instruções por relógio, de acordo comperf
), etail
usou 0,162 da CPU na qual executou (0,58 IPC). Caso contrário, o sistema estava praticamente inativo.Estou usando o Linux 4.14.11-1-ARCH, que tem o KPTI ativado por padrão para solucionar o Meltdown, portanto, todas as
write
chamadas de sistemagzip
são mais caras do que costumavam ser: /Ter a busca incorporada
unzip
ouzcat
(mas ainda usando azlib
função de decodificação regular ) salvaria todas essas gravações de pipe e faria com que as CPUs Skylake funcionassem a toda velocidade. (Esse downclock para alguns tipos de carga é exclusivo do Intel Skylake e posterior, que descarregou a tomada de decisão da frequência da CPU do SO, porque eles têm mais dados sobre o que a CPU está fazendo e podem aumentar / diminuir mais rapidamente. normalmente bom, mas aqui a Skylake não aumenta sua velocidade máxima com uma configuração de governador mais conservadora).Nenhuma chamada do sistema, apenas reescrever um buffer que se encaixa no cache L2 até você alcançar a posição de byte inicial desejada, provavelmente faria pelo menos uma diferença de alguns%. Talvez até 10%, mas estou inventando números aqui. Não detalhei
zlib
detalhadamente o tamanho de um espaço ocupado em cache e o quanto a liberação TLB (e, portanto, a liberação de cache de cache) em todas as chamadas do sistema prejudica o KPTI ativado.Existem alguns projetos de software que adicionam um índice de busca ao formato de arquivo gzip . Isso não ajuda se você não conseguir que alguém gere arquivos compactados procuráveis para você, mas outros futuros leitores podem se beneficiar.
Presumivelmente, nenhum desses projetos tem uma função de decodificação que sabe pular um fluxo Deflate sem um índice, porque eles são projetados para funcionar apenas quando um índice está disponível.
fonte
Você pode abrir o arquivo zip em uma sessão python,
zf = zipfile.ZipFile(filename, 'r', allowZip64=True)
e, uma vez aberto, pode abrir, para leitura, qualquer arquivo dentro do arquivo zip e linhas de leitura, etc., como se fosse um arquivo normal.fonte