divide o arquivo de texto grande (10GB) com o comando split a) com um tamanho mínimo do arquivo de saída (40MB) eb) depois de uma string específica (/ registro)

1

Sou novo na casca e não tenho ninguém para perguntar. Muito obrigado!

Eu obtive um grande arquivo de texto (10GB, .xml, contém mais de 1 milhão de tags como esta: <record> text </record> ) que eu dividi em partes para usá-lo. Mas, para poder automatizar meu processo de trabalho, é necessário que cada parte termine com uma tag específica: </record> . E também é necessário que cada parte tenha pelo menos o tamanho de cerca de 40MB.

    
por Michael Käfer 21.11.2014 / 12:35

1 resposta

1

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:

  1. 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).
  2. 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).
por Jacob Vlijm 24.11.2014 / 10:33