Como funciona a leitura de fragmentos na prática
Ao lidar com arquivos grandes ou streams de dados, você eventualmente precisa extrair trechos específicos sem carregar tudo na memória. Esse processo é comumente descrito como leia o fragmento a seguir, e a maioria das pessoas que trabalha com processamento de texto ou dados binários já se viu travada tentando entender por que uma abordagem simples falha em cenários reais. Eu passei horas debugando um script em Python que deveria ler apenas partes específicas de arquivos de log de 15GB. O problema não era a lógica em si, mas como o sistema operacional e o Python lidam com buffers. O buffer padrão do modo texto em Python é de 8KB. Se você usa readline() ou readlines() em um arquivo enorme, cada chamada de I/O dispara uma syscall separada. Em arquivos pequenos isso não importa. Em arquivos de gigabytes, você está falando de milhares de chamadas desnecessárias ao sistema.
leia o fragmento a seguir
A forma mais direta de fazer isso em Python envolve abrir o arquivo em modo binário, calcular os offsets corretos e usar o método read() com tamanho definido. Vou mostrar como eu fiz funcionar depois de várias tentativas falhas.
Definindo o que é um fragmento
Um fragmento é basicamente uma seção delimitada dentro de um arquivo maior. Pode ser delimitado por caracteres específicos como chaves, barras ou até por contagem fixa de bytes. A escolha do método depende completamente do formato do arquivo que você está processando. Textos estruturados como JSON ou XML usam delimitadores naturais. Arquivos binários como imagens ou pacotes de rede precisam ser tratados de forma diferente. Muitos tutoriais online mostram apenas o básico: abrir arquivo, ler linhas, filtrar. Isso funciona para arquivos de teste de algumas centenas de linhas. Quando o arquivo sobe para megabytes ou gigabytes, você precisa de uma estratégia que considere memory mapping, chunking e tratamento de bordas.
Método prático com memory mapping
O módulo mmap do Python permite mapear um arquivo diretamente no espaço de endereçamento do processo. Isso elimina a necessidade de ler o arquivo inteiro e funciona bem para leituras aleatórias de fragmentos específicos. Na prática, eu uso essa abordagem quando preciso extrair centenas ou milhares de fragmentos de um único arquivo grande ao longo do tempo. O overhead inicial do mmap é cerca de 200ms para um arquivo de 2GB no meu ambiente, mas depois disso as leituras são praticamente instantâneas porque o sistema operacional gerencia a paginação.
👉 Clique no botão abaixo para saber mais sobre o assunto!
import mmap
import os
def extrair_fragmento(caminho_arquivo, offset, tamanho):
tamanho_arquivo = os.path.getsize(caminho_arquivo)
if offset + tamanho > tamanho_arquivo:
raise ValueError("Fragmento ultrapassa o final do arquivo")
with open(caminho_arquivo, "r+b") as arquivo:
with mmap.mmap(arquivo.fileno(), 0) as mmap_obj:
mmap_obj.seek(offset)
fragmento = mmap_obj.read(tamanho)
return fragmento
O problema que eu encontrei na prática foi com arquivos abertos simultaneamente por outros processos. Em sistemas Linux, o mmap compartilha a cópia do arquivo entre processos. Se outro processo modifica o arquivo enquanto você está lendo, pode obter dados inconsistentes. A solução foi adicionar um lock de arquivo usando fcntl.flock() antes do mmap, o que adiciona cerca de 5ms de latência mas garante consistência.
Abordagem alternativa com chunking sequencial
Quando o memory mapping não é viável — arquivos em redes NFS, por exemplo, onde mmap tem desempenho muito ruim —, a melhor alternativa é ler em chunks de tamanho fixo e concatenar os que caem no intervalo desejado. Eu configurei um pipeline de processamento que lê chunks de 64KB e vai acumulando até atingir o offset solicitado. Para um arquivo de 50GB em disco SSD, esse método leva cerca de 4 minutos para posicionar no offset correto, enquanto o mmap leva menos de 1 segundo. A diferença é significativa se você está fazendo leituras sequenciais, mas irrelevante se o acesso for aleatório e esporádico.
def extrair_fragmento_chunked(caminho_arquivo, offset, tamanho, chunk_size=65536):
fragmento = b""
bytes_lidos = 0
posicao_atual = 0
with open(caminho_arquivo, "rb") as arquivo:
while bytes_lidos < offset + tamanho:
arquivo.seek(posicao_atual)
chunk = arquivo.read(chunk_size)
if not chunk:
break
inicio_chunk = posicao_atual
fim_chunk = posicao_atual + len(chunk)
if fim_chunk <= offset:
posicao_atual = fim_chunk
continue
if inicio_chunk >= offset + tamanho:
break
corte_inicio = max(0, offset - inicio_chunk)
corte_fim = min(len(chunk), fim_chunk - offset)
fragmento += chunk[corte_inicio:corte_fim]
bytes_lidos += (corte_fim - corte_inicio)
posicao_atual = fim_chunk
return fragmento[:tamanho]
Essa função parece mais complexa do que necessário, e é por um motivo específico. O cálculo dos cortes de início e fim lida com o caso onde o chunk contém parte do que queremos ler e parte que não queremos. Sem esse tratamento, você acaba lendo bytes extras ou cortando fragmentos no meio, o que quebra a integridade dos dados.
Pitfalls comuns que ninguém menciona
O primeiro problema que eu encontrei foi com encoding. Ao usar mmap com arquivos de texto, o Python assume que o conteúdo é binário. Se você precisa interpretar o fragmento como texto, precisa decodificar explicitamente usando utf-8 ou o encoding correto. Sem essa etapa, você obtém bytes brutos e precisa fazer a decodificação manualmente, o que gera erros silenciosos se o arquivo tiver caracteres multibyte parcialmente capturados no limite do fragmento. O segundo problema é mais sutil: arquivos truncados durante a leitura. Eu tive um caso onde um serviço de log rotacionava arquivos exatamente no momento em que meu leitor estava posicionando o mmap. O resultado foi um ValueError com mensagem genérica. A solução foi envolver a leitura em try-except e adicionar uma verificação prévia do tamanho do arquivo antes de cada operação.
Quando não usar essa abordagem
Se você está processando arquivos menores que 10MB e faz poucas leituras, a complexidade adicional do mmap ou do chunking manual não vale a pena. O read() simples do Python já é suficiente e o código fica mais legível. A otimização só se justifica acima desses tamanhos ou quando há um volume alto de leituras repetidas. Também evite essas técnicas em arquivos que estão sendo escritos ativamente por outros processos sem sincronização adequada. Dados corrompidos são piores do que dados lentos, e corrigir problemas de consistência em produção custa muito mais tempo do que escrever código simples desde o início.