O módulo bz2 no Python permite comprimir e descomprimir dados com o algoritmo bzip2 sem instalar uma biblioteca externa. Ele atende desde tarefas pequenas, como compactar uma sequência de bytes em memória, até fluxos maiores que precisam ser processados em blocos. Também oferece uma interface de arquivo compatível com o protocolo de contexto, facilitando leitura e escrita com with.
Bzip2 costuma produzir arquivos menores que gzip em dados textuais repetitivos, embora normalmente consuma mais CPU. Por isso, a escolha deve considerar tamanho, velocidade, compatibilidade e o ambiente que receberá o arquivo. Este guia mostra as APIs principais, os cuidados com memória e as diferenças entre processamento de uma vez e incremental.
Quando usar bz2
Use bz2 quando o formato .bz2 for exigido por um sistema existente, quando você desejar boa taxa de compressão em logs ou textos, ou quando precisar integrar dados produzidos por ferramentas Unix. Para arquivos que serão abertos frequentemente por usuários comuns, ZIP ou gzip podem ser mais convenientes. Para conhecer uma alternativa amplamente suportada, veja o guia sobre zlib no Python.
Compressão rápida em memória
A função bz2.compress() recebe um objeto de bytes e devolve os bytes comprimidos. O nível varia de 1 a 9: valores maiores favorecem tamanho, mas podem gastar mais tempo.
import bz2
dados = ("linha de relatório\n" * 1000).encode("utf-8")
compactado = bz2.compress(dados, compresslevel=9)
restaurado = bz2.decompress(compactado)
assert restaurado == dados
print(len(dados), len(compactado))Essa abordagem é simples, porém mantém entrada e saída inteiras na memória. Para arquivos grandes, prefira a interface de arquivo ou os objetos incrementais. O artigo sobre leitura de arquivos gigantes apresenta estratégias complementares para evitar picos de RAM.
Grave um arquivo .bz2
bz2.open() funciona de maneira semelhante a open(). Em modo binário, escreva bytes; em modo texto, informe codificação explicitamente.
import bz2
with bz2.open("relatorio.txt.bz2", "wt", encoding="utf-8") as arquivo:
arquivo.write("produto,quantidade\n")
arquivo.write("teclado,12\n")
arquivo.write("monitor,4\n")O modo wt cria ou substitui o arquivo. Use xt para falhar quando o destino já existir e evitar sobrescrita acidental. O modo at acrescenta um novo fluxo comprimido ao final; a leitura com BZ2File consegue processar fluxos concatenados.
Leia em modo texto
import bz2
with bz2.open("relatorio.txt.bz2", "rt", encoding="utf-8") as arquivo:
for numero, linha in enumerate(arquivo, start=1):
print(numero, linha.rstrip())A iteração é adequada para textos extensos porque entrega uma linha por vez. Trate UnicodeDecodeError quando a codificação for desconhecida. Não use errors="ignore" automaticamente, pois isso pode apagar caracteres e alterar dados silenciosamente.
BZ2File e objetos de arquivo
A classe BZ2File trabalha em modo binário e aceita tanto um caminho quanto um arquivo já aberto. Isso é útil quando outra camada controla o armazenamento.
from bz2 import BZ2File
from pathlib import Path
caminho = Path("dados.bin.bz2")
with BZ2File(caminho, "wb", compresslevel=7) as destino:
destino.write(b"cabecalho\x00")
destino.write(b"conteudo" * 500)A classe implementa grande parte de io.BufferedIOBase, incluindo leitura, escrita, busca quando possível e readinto(). Ela não é segura para leitores ou escritores simultâneos compartilhando a mesma instância. Proteja o acesso com sincronização ou dê uma instância independente a cada tarefa.
Compressão incremental
Quando os dados chegam em blocos, use BZ2Compressor. Cada chamada pode devolver bytes ou uma sequência vazia, porque o compressor mantém buffers internos. No final, flush() é obrigatório.
import bz2
compressor = bz2.BZ2Compressor(compresslevel=6)
partes = []
for bloco in gerar_blocos():
saida = compressor.compress(bloco)
if saida:
partes.append(saida)
partes.append(compressor.flush())
resultado = b"".join(partes)Depois de flush(), o objeto não pode ser reutilizado. Crie outro compressor para um novo fluxo.
Descompressão incremental com limite
BZ2Decompressor.decompress() aceita max_length, permitindo limitar a saída produzida por chamada. Esse controle é importante ao lidar com conteúdo externo, pois poucos bytes comprimidos podem se expandir muito.
import bz2
dec = bz2.BZ2Decompressor()
saida = bytearray()
limite_total = 20_000_000
for bloco in receber_blocos():
pendente = bloco
while pendente or not dec.needs_input:
trecho = dec.decompress(pendente, max_length=64 * 1024)
pendente = b""
saida.extend(trecho)
if len(saida) > limite_total:
raise ValueError("conteúdo descompactado excedeu o limite")
if dec.eof:
breakQuando needs_input é falso, o descompressor ainda consegue produzir dados sem nova entrada. unused_data guarda bytes encontrados depois do fim do fluxo. Diferentemente de bz2.decompress() e BZ2File, uma única instância incremental não atravessa automaticamente vários fluxos concatenados.
Valide o fim do fluxo
Não considere a operação concluída apenas porque uma chamada retornou bytes. Verifique eof para distinguir um fluxo completo de um arquivo truncado. Trate OSError, EOFError e erros de entrada conforme a API usada.
Evite arquivos compactados maliciosos
Compressão não autentica o conteúdo. Antes de processar dados externos, limite tamanho comprimido, tamanho expandido, quantidade de arquivos, tempo de CPU e profundidade de etapas. Salve em diretório controlado e nunca use o nome fornecido pelo usuário sem normalização. Se o arquivo tiver estrutura de pacote, as mesmas precauções discutidas no guia de arquivos ZIP com Python continuam relevantes.
Escolha o nível de compressão
O nível 9 é o padrão, mas não é automaticamente a melhor escolha. Meça com dados reais. Em pipelines frequentes, nível 5 ou 6 pode oferecer melhor equilíbrio. Dados já comprimidos, como JPEG, MP4 e muitos PDFs, tendem a encolher pouco e apenas desperdiçam CPU.
Compare com gzip, lzma e zlib
- gzip: geralmente mais rápido e muito compatível.
- bz2: boa compressão para texto e arquivos históricos.
- lzma: pode gerar arquivos ainda menores, com maior custo.
- zlib: indicado para fluxos e protocolos que usam DEFLATE.
O objetivo do arquivo determina a escolha. Para backups de diretórios, um contêiner como TAR combina vários itens antes da compressão. Para dados estruturados de aplicação, mantenha formato e compressão como responsabilidades separadas.
Teste a ida e a volta
Um teste essencial comprime, descomprime e compara os bytes originais. Inclua arquivo vazio, Unicode, dados aleatórios, conteúdo repetitivo, entradas truncadas, níveis diferentes e vários fluxos concatenados.
import bz2
import pytest
@pytest.mark.parametrize("dados", [b"", b"abc", b"x" * 100_000])
def test_round_trip(dados):
assert bz2.decompress(bz2.compress(dados)) == dadosBoas práticas
- Use modo texto com codificação explícita.
- Use
withpara fechar arquivos. - Prefira processamento incremental para grandes volumes.
- Chame
flush()exatamente uma vez no compressor. - Verifique
eofem entradas parciais. - Imponha limite à saída descompactada.
- Não compartilhe a mesma instância entre threads.
- Meça nível e algoritmo com dados reais.
Conclusão
O bz2 no Python oferece uma interface completa para bzip2: funções simples, arquivos em modo texto ou binário e objetos incrementais. A API é direta, mas o uso robusto exige atenção a memória, fim de fluxo, streams concatenados e expansão de conteúdo não confiável.
Consulte a documentação oficial do bz2 e o projeto bzip2. Para organizar opções de aplicações que manipulam esses arquivos, veja também configparser no Python.







