O script abaixo divide um arquivo (grande) em fatias. Não usei o comando split
, pois o conteúdo do seu arquivo precisa ser "arredondado" pelos registros. O tamanho das fatias que você pode definir na seção principal do script.
O procedimento
Dificuldades
Como o script deve ser capaz de lidar com arquivos grandes, o read()
ou readlines()
do python não pode ser usado; o script tentaria carregar o arquivo inteiro de uma vez na memória, o que certamente sufocaria seu sistema. Ao mesmo tempo, as divisões têm que ser feitas, "arredondando" seções por um registro inteiro. O script deve, portanto, ser capaz de identificar ou "ler" o conteúdo do arquivo.
O que parece ser a única opção é usar:
with open(file) as src:
for line in src:
que lê o arquivo linha por linha.
Abordagem
No script, escolhi uma abordagem em duas etapas:
- Analise o arquivo (tamanho, número de fatias, número de linhas, número de registros, registros por seção) e crie uma lista de seções ou "marcadores" (por índice de linha).
- lendo o arquivo novamente, mas agora alocando as linhas para separar arquivos.
O procedimento para anexar as linhas às fatias separadas (arquivos), uma a uma, parece ineficiente, mas de tudo o que tentei, acabou sendo a opção mais eficiente, mais rápida e menos consumidora.
Como eu testei
Eu criei um arquivo xml
de pouco mais de 10 GB, preenchido com registros como seu exemplo. Eu configurei o tamanho das fatias para 45mb
. No meu sistema não tão recente (CPU Pentium Dual-Core E6700 @ 3.20GHz × 2), a análise do script produziu o seguinte:
analyzing file...
checking file size...
file size: 10767 mb
calculating number of slices...
239 slices of 45 mb
checking number of lines...
number of lines: 246236399
checking number of records...
number of records: 22386000
calculating number records per section ...
records per section: 93665
Então começou a criar fatias de 45 mb, tomando appr. 25 a 27 segundos por fatia para criar.
creating slice 1
creating slice 2
creating slice 3
creating slice 4
creating slice 5
e assim por diante ...
O processador foi ocupado por 45-50% durante o processo, usando ~ 850-880mb da minha memória (de 4GB). O computador era razoavelmente utilizável durante o processo.
Todo o procedimento levou uma hora e meia. Em um sistema mais recente, deve levar muito menos tempo.
O script
#!/usr/bin/env python3
import os
import time
#---
file = "/path/to/big/file.xml"
out_dir = "/path/to/save/slices"
size_ofslices = 45 # in mb
identifying_string = "</record>"
#---
line_number = -1
records = [0]
# analyzing file -------------------------------------------
print("analyzing file...\n")
# size in mb
print("checking file size...")
size = int(os.stat(file).st_size/1000000)
print("file size:", size, "mb")
# number of sections
print("calculating number of slices...")
sections = int(size/size_ofslices)
print(sections, "slices of", size_ofslices, "mb")
# misc. data
print("checking number of lines...")
with open(file) as src:
for line in src:
line_number = line_number+1
if identifying_string in line:
records.append(line_number)
# last index (number of lines -1)
ns_oflines = line_number
print("number of lines:", ns_oflines)
# number of records
print("checking number of records...")
ns_records = len(records)-1
print("number of records:", ns_records)
# records per section
print("calculating number records per section ...")
ns_recpersection = int(ns_records/sections)
print("records per section:", ns_recpersection)
# preparing data -------------------------------------------
rec_markers = [i for i in range(ns_records) if i% ns_recpersection == 0]+[ns_records] # dividing records (indexes of) in slices
line_markers = [records[i] for i in rec_markers] # dividing lines (indexes of) in slices
line_markers[-1] = ns_oflines; line_markers.pop(-2) # setting lias linesection until last line
# creating sections ----------------------------------------
sl = 1
line_number = 0
curr_marker = line_markers[sl]
outfile = out_dir+"/"+"slice_"+str(sl)+".txt"
def writeline(outfile, line):
with open(outfile, "a") as out:
out.write(line)
with open(file) as src:
print("creating slice", sl)
for line in src:
if line_number <= curr_marker:
writeline(outfile, line)
else:
sl = sl+1
curr_marker = line_markers[sl]
outfile = out_dir+"/"+"slice_"+str(sl)+".txt"
print("creating slice", sl)
writeline(outfile, line)
line_number = line_number+1
Como usar
Copie o script em um arquivo vazio, defina o caminho para o "arquivo grande", o caminho para o diretório para salvar as fatias e o tamanho das fatias. Salve como slice.py
e execute-o pelo comando:
/path/to/slice.py
Notas
- O tamanho do arquivo grande deve exceder o tamanho da fatia pelo menos algumas vezes. Quanto maior a diferença, mais confiável será o tamanho das fatias (de saída).
- A suposição foi feita de que o tamanho médio dos registros (visto na foto maior) é praticamente o mesmo. Olhando para a enorme quantidade de dados aqui seria de se esperar que seria uma suposição aceitável, mas você terá que verificar (observando se há uma grande diferença no tamanho das fatias).