Разделить текстовый файл 10 ГБ 1) с минимальным размером выходных файлов 40 МБ и 2) после определенной строки (</ record>)

Я получил большой текстовый файл (10 ГБ, .xml, содержащий более 1 миллиона тегов, например: <record>текст</record>), который я разделил на части, чтобы использовать его. Но чтобы автоматизировать мой рабочий процесс, необходимо, чтобы каждая часть заканчивалась определенным тегом: </record>, И также необходимо, чтобы каждая часть имела размер не менее 40 МБ.

1 ответ

Решение

Сценарий ниже нарезает (большой) файл на кусочки. Я не использовал split команда, так как содержимое вашего файла должно быть "округлено" по записям. Размер ломтиков вы можете установить в разделе заголовка скрипта.

Процедура

Трудности
Поскольку скрипт должен иметь дело с огромными файлами, либо Python read() или же readlines() не может быть использован; скрипт будет пытаться загрузить весь файл сразу в память, что наверняка задушит вашу систему. В то же время делаются деления, "округляя" участки по целой записи. Следовательно, скрипт должен каким-то образом определять или "читать" содержимое файла.

То, что кажется единственным вариантом, это использовать:

with open(file) as src:
    for line in src:

который читает файл строка за строкой.

Подход
В сценарии я выбрал двухэтапный подход:

  1. Проанализируйте файл (размер, количество срезов, количество строк, количество записей, записей на раздел), затем создайте список разделов или "маркеров" (по индексу строк).
  2. снова читаем файл, но теперь выделяем строки для отдельных файлов.

Процедура добавления строк в отдельные фрагменты (файлы) один за другим кажется неэффективной, но из всех, что я пробовал, оказался наиболее эффективным, быстрым и наименее затратным вариантом.

Как я проверял
Я создал xml файл размером чуть более 10 ГБ, заполненный записями, подобными вашему примеру. Я установил размер ломтиков на 45mb, В моей не совсем новой системе (двухъядерный процессор Pentium E6700 @ 3,20 ГГц × 2) анализ сценария показал следующее:

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

Затем он начал создавать кусочки по 45 мб, взяв ок. 25-27 секунд на срез для создания.

creating slice 1
creating slice 2
creating slice 3
creating slice 4
creating slice 5

и так далее...

Процессор был занят на 45-50 % во время процесса, используя ~850-880 МБ моей памяти (4 ГБ). Компьютер был приемлемым для использования во время процесса.

Вся процедура заняла полтора часа. В более новой системе это должно занять значительно меньше времени.

Сценарий

#!/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 

Как пользоваться

Скопируйте скрипт в пустой файл, укажите путь к вашему "большому файлу", путь к каталогу для сохранения ломтиков и размер ломтиков. Сохранить как slice.py и запустите его командой:

/path/to/slice.py

Заметки

  • Размер большого файла должен превышать размер среза как минимум в несколько раз. Чем больше разница, тем надежнее будет размер (выходных) срезов.
  • Было сделано предположение, что средний размер записей (на более широкой картине) примерно одинаков. Глядя на огромное количество данных здесь, можно было бы ожидать, что это будет приемлемым предположением, но вам придется проверить (посмотрев, есть ли большая разница в размере срезов).
Другие вопросы по тегам