Divisão de Arquivos Gigantes em Partes Menores (Chunking) em Python: Guia Prático

Divisão de Arquivos Gigantes em Partes Menores (Chunking) em Python: Guia Prático

A manipulação de arquivos de texto e dados com dimensões que variam de 1 GB a dezenas de gigabytes é um desafio recorrente na rotina de desenvolvedores e engenheiros de dados. Quando um arquivo excede a capacidade de memória RAM disponível ou ultrapassa o limite suportado por editores de texto convencionais, a tentativa de carregá-lo integralmente gera falhas catastróficas, como erros de estouro de memória (MemoryError). A solução técnica para esse problema consiste no processo de chunking, ou seja, na divisão programática do arquivo bruto em partes menores e gerenciáveis.

Python oferece múltiplos mecanismos para realizar essa segmentação sem comprometer o desempenho do sistema. A escolha da abordagem ideal depende do critério de divisão desejado — se baseado em um número fixo de linhas ou em um tamanho específico em bytes — e do nível de integração nativa ou de dependências externas necessário para a aplicação.

1. Divisão por Número de Linhas com Python Nativo

A forma mais segura e eficiente de processar arquivos massivos com as ferramentas padrão da linguagem é iterar sobre o arquivo de entrada como um fluxo continuado (streaming). Em vez de utilizar métodos como readlines(), que carregam todo o conteúdo na memória de uma só vez, a iteração linha a linha garante que apenas um registro permaneça na RAM a cada instante.

Para dividir um arquivo em lotes com um número definido de linhas, combina-se a leitura sequencial do descritor de arquivo com um laço de gravação dos blocos.

def dividir_arquivo_por_linhas(caminho_entrada, linhas_por_arquivo):
    nome_base, extensao = caminho_entrada.rsplit('.', 1)
    contador_arquivo = 1
    
    with open(caminho_entrada, 'r', encoding='utf-8') as arquivo_origem:
        enquanto_houver_conteudo = True
        
        while enquanto_houver_conteudo:
            caminho_saida = f"{nome_base}_parte_{contador_arquivo}.{extensao}"
            
            with open(caminho_saida, 'w', encoding='utf-8') as arquivo_destino:
                linhas_escritas = 0
                for _ in range(linhas_por_arquivo):
                    linha = arquivo_origem.readline()
                    if not linha:
                        enquanto_houver_conteudo = False
                        break
                    arquivo_destino.write(linha)
                    linhas_escritas += 1
                
                if linhas_escritas > 0:
                    contador_arquivo += 1

# Exemplo de uso: dividir em partes de 100.000 linhas cada
dividir_arquivo_por_linhas("grande_arquivo.txt", 100000)

Essa arquitetura garante que a memória seja mantida limpa independentemente de o arquivo original possuir 1 GB ou 100 GB. O ponteiro interno do arquivo de entrada avança continuamente a cada chamada de readline(), preservando o estado da leitura entre os arquivos de saída gerados.

2. Divisão por Tamanho Fixo em Bytes (Buffer Binário)

Em cenários nos quais a estrutura de linhas não é prioritária ou quando se lida com arquivos binários e dados não estruturados, a divisão por peso (tamanho em bytes) é a estratégia recomendada. Nesses casos, o arquivo é aberto no modo de leitura binária (rb), permitindo ler blocos fixos de dados utilizando um buffer de tamanho controlado.

A técnica consiste em ler pedaços de dados do arquivo original com read(tamanho_buffer) e gravá-los sucessivamente em novos arquivos até que o limite estipulado para cada parte seja atingido.

import os

def obter_tamanho_arquivo(caminho):
    with open(caminho, "rb") as arquivo:
        arquivo.seek(0, os.SEEK_END)
        return arquivo.tell()

def dividir_arquivo_por_tamanho(caminho_entrada, tamanho_parte_kb, tamanho_buffer_kb=5):
    tamanho_parte_bytes = tamanho_parte_kb * 1000
    tamanho_buffer_bytes = tamanho_buffer_kb * 1000
    
    tamanho_total = obter_tamanho_arquivo(caminho_entrada)
    numero_partes = tamanho_total // tamanho_parte_bytes
    
    nome_base, extensao = caminho_entrada.rsplit('.', 1)
    contador_parte = 1
    
    with open(caminho_entrada, "rb") as arquivo_origem:
        for i in range(numero_partes + 1):
            contador_chunks = tamanho_parte_bytes // tamanho_buffer_bytes
            dados_buffer = arquivo_origem.read(tamanho_buffer_bytes)
            
            caminho_saida = f"{nome_base}_parte_{contador_parte}.{extensao}"
            
            with open(caminho_saida, "wb") as arquivo_destino:
                while dados_buffer:
                    arquivo_destino.write(dados_buffer)
                    if contador_chunks > 0:
                        contador_chunks -= 1
                        dados_buffer = arquivo_origem.read(tamanho_buffer_bytes)
                    else:
                        break
            contador_parte += 1

# Exemplo de uso: dividir em partes de 50.000 KB (50 MB)
dividir_arquivo_por_tamanho("dados_massivos.bin", 50000)

Nesse modelo, o deslocamento do ponteiro com seek() e a leitura com tell() auxiliam no monitoramento do tamanho total do arquivo, garantindo uma divisão precisa dos blocos binários sem sobrecarregar a memória do sistema.

3. Automação Simplificada com a Biblioteca filesplit

Para desenvolvedores que buscam uma solução pronta para produção sem a necessidade de escrever laços manuais de controle de arquivo, o ecossistema PyPI disponibiliza o pacote filesplit. Ele abstrai a complexidade do gerenciamento de buffers e ponteiros, oferecendo uma API orientada a objetos para divisão por linhas ou tamanho.

Após realizar a instalação do pacote via gerenciador de pacotes (pip install filesplit), a biblioteca pode ser instanciada informando o arquivo de entrada e o diretório de destino das partes.

from filesplit.split import Split

# Instancia o objeto informando o arquivo de origem e a pasta de destino
split = Split("meu_arquivo_gigante.txt", "pasta_saida")

# Opção A: Divisão por número de linhas por arquivo
split.bylinecount(10000)

# Opção B: Divisão por tamanho em bytes por arquivo (ex: 18.000 bytes)
# split.bysize(18000)

A biblioteca filesplit cuida automaticamente do particionamento e da nomeação dos arquivos gerados no diretório de destino, tornando-se uma opção conveniente para rotinas de automação rápida e integração em pipelines de dados existentes.

4. Integração com Utilitários de Sistema via subprocess

Em ambientes Unix/Linux e macOS (ou no Windows com Cygwin/WSL), o utilitário nativo de linha de comando split oferece altíssimo desempenho para divisão de arquivos diretamente no nível do sistema operacional. Python pode delegar a tarefa a essa ferramenta do sistema por meio do módulo subprocess, combinando a velocidade do binário compilado da plataforma com o controle de fluxo do script Python.

import subprocess
import os

def dividir_com_utilitario_sistema(caminho_arquivo, tamanho_bloco_linhas=50000):
    diretorio_destino = os.path.dirname(caminho_arquivo) or "."
    prefixo_saida = os.path.join(diretorio_destino, "chunk_")
    
    # Executa o comando de sistema 'split'
    # -a 2: sufixo de 2 caracteres (aa, ab, ac...)
    # -l: quantidade de linhas por arquivo
    subprocess.call([
        "split",
        "-a", "2",
        "-l", str(tamanho_bloco_linhas),
        caminho_arquivo,
        prefixo_saida
    ])

# Exemplo de execução
dividir_com_utilitario_sistema("registro_servidor.log", 50000)

A utilização do subprocesso com o utilitário do sistema reduz o custo de interpretação do Python, atingindo taxas elevadas de vazão de I/O. No entanto, deve-se atentar para o fato de que a execução do subprocess cria uma bifurcação de processo com pegada de memória associada, devendo ser monitorada em ambientes restritos.

5. Comparativo das Abordagens de Chunking

A escolha do método ideal varia de acordo com as restrições de infraestrutura, os requisitos de integridade das linhas e a presença de dependências externas no ambiente de execução.

AbordagemCritério de DivisãoConsumo de MemóriaDependênciasCenário Ideal
Streaming Nativo por LinhasContagem de LinhasMínimo (Constante)Apenas Python NativoArquivos de texto estruturados (CSV, LOG, TXT) onde a quebra de linha precisa ser respeitada.
Buffer Binário Nível de BytesPeso em BytesMínimo (Buffer fixo)Apenas Python NativoArquivos binários, arquivos sem quebra de linha clara ou quando se deseja tamanho exato de bloco.
Biblioteca filesplitLinhas ou BytesBaixoPacote externo PyPIAutomação rápida, código limpo e abstração completa da manipulação de arquivos.
Comando de Sistema splitLinhas ou BytesBaixoUtilitário OS e subprocessAmbientes Linux/Unix que priorizam máxima velocidade de I/O sem codificação manual de loops.

6. Diretrizes de Segurança e Tratamento de Recursos

Para garantir a estabilidade em ambientes de produção ao dividir arquivos massivos, é recomendado adotar as seguintes práticas de engenharia:

  1. Uso Estrito de Context Managers (with): Garante o fechamento automático de descritores de arquivos e a liberação de recursos do sistema operacional mesmo na ocorrência de exceções durante o laço de leitura/escrita.
  2. Especificação do Encoding: Ao manipular arquivos de texto, explicite sempre o parâmetro encoding='utf-8' para evitar corrupção de caracteres em sistemas operacionais com codificação padrão divergente.
  3. Gerenciamento de Espaço em Disco: Certifique-se de que o volume de armazenamento de destino possui espaço livre suficiente para acomodar a cópia dividida do arquivo bruto antes de iniciar a execução da rotina.

Thales de Oliveira Gomes

Thales de Oliveira Gomes é gestor de Tecnologia da Informação, especialista em infraestrutura de redes, computação em nuvem e operações de TI hospitalar, com mais de 18 anos de experiência na área. Graduado em Gestão de TI e pós-graduado em Engenharia de Redes e Telecomunicações, Cloud Computing, Redes de Computadores e Gestão Hospitalar, construiu uma trajetória sólida liderando projetos de alta complexidade, como a implementação e migração para nuvem de sistemas ERP de grande porte. Sua atuação no setor o levou a ser indicado ao prêmio IT Leaders Brasil na categoria Rising Leaders. Além da carreira corporativa e da paixão por tecnologia, Thales é autor independente com mais de 70 obras publicadas sobre redes, segurança da informação, Linux e física, além de criador de conteúdo no blog Vetor Tech e na plataforma CiberDicas. No universo das artes e do entretenimento, atua como produtor de música eletrônica sob o projeto Yknex, cria ilustrações digitais e compartilha walkthroughs de games no YouTube.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *