Разделение большого текстового файла на более мелкие текстовые файлы по номерам строк с использованием Python

У меня есть текстовый файл say really_big_file.txt, который содержит:

line 1
line 2
line 3
line 4
...
line 99999
line 100000

Я хотел бы написать Python script, который делит файл real_big_file.txt на более мелкие файлы по 300 строк. Например, small_file_300.txt имеет строки 1-300, small_file_600 для строк 301-600 и т.д., Пока не будет достаточно маленьких файлов, сделанных для того, чтобы содержать все строки из большого файла.

Я был бы признателен за любые предложения по простейшему способу выполнения этого с помощью Python

Ответ 1

Использование itertools grouper рецепт:

from itertools import izip_longest

def grouper(n, iterable, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

n = 300

with open('really_big_file.txt') as f:
    for i, g in enumerate(grouper(n, f, fillvalue=''), 1):
        with open('small_file_{0}'.format(i * n), 'w') as fout:
            fout.writelines(g)

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

Обратите внимание, что последний файл в этом случае будет small_file_100200, но будет работать только до line 100000. Это происходит потому, что fillvalue='', что означает, что я ничего не записываю в файл, когда у меня больше нет строк для записи, потому что размер группы не делится одинаково. Вы можете исправить это, написав временный файл, а затем переименовав его, вместо того, чтобы называть его первым, как я. Вот как это можно сделать.

import os, tempfile

with open('really_big_file.txt') as f:
    for i, g in enumerate(grouper(n, f, fillvalue=None)):
        with tempfile.NamedTemporaryFile('w', delete=False) as fout:
            for j, line in enumerate(g, 1): # count number of lines in group
                if line is None:
                    j -= 1 # don't count this line
                    break
                fout.write(line)
        os.rename(fout.name, 'small_file_{0}.txt'.format(i * n + j))

В этот раз fillvalue=None и я просматриваю каждую строку, проверяя None, когда это происходит, я знаю, что процесс завершился, поэтому я вычитаю 1 из j, чтобы не считать наполнитель, а затем написать файл.

Ответ 2

lines_per_file = 300
smallfile = None
with open('really_big_file.txt') as bigfile:
    for lineno, line in enumerate(bigfile):
        if lineno % lines_per_file == 0:
            if smallfile:
                smallfile.close()
            small_filename = 'small_file_{}.txt'.format(lineno + lines_per_file)
            smallfile = open(small_filename, "w")
        smallfile.write(line)
    if smallfile:
        smallfile.close()

Ответ 3

import csv
import os
import re

MAX_CHUNKS = 300


def writeRow(idr, row):
    with open("file_%d.csv" % idr, 'ab') as file:
        writer = csv.writer(file, delimiter=',', quotechar='\"', quoting=csv.QUOTE_ALL)
        writer.writerow(row)

def cleanup():
    for f in os.listdir("."):
        if re.search("file_.*", f):
            os.remove(os.path.join(".", f))

def main():
    cleanup()
    with open("large_file.csv", 'rb') as results:
        r = csv.reader(results, delimiter=',', quotechar='\"')
        idr = 1
        for i, x in enumerate(r):
            temp = i + 1
            if not (temp % (MAX_CHUNKS + 1)):
                idr += 1
            writeRow(idr, x)

if __name__ == "__main__": main()

Ответ 4

Я делаю это более понятным способом и использую менее короткие сокращения, чтобы дать вам дополнительное представление о том, как и почему это работает. Предыдущие ответы работают, но если вы не знакомы с определенными встроенными функциями, вы не поймете, что делает функция.

Поскольку вы не размещали код, я решил сделать это так, потому что вы могли быть незнакомы с вещами, отличными от базового синтаксиса python, учитывая, что, как вы сформулировали этот вопрос, казалось, что вы не пытались и не знали, как подойти к вопросу

Ниже приведены шаги для этого в базовом python:

Сначала вы должны прочитать свой файл в списке для хранения:

my_file = 'really_big_file.txt'
hold_lines = []
with open(my_file,'r') as text_file:
    for row in text_file:
        hold_lines.append(row)

Во-вторых, вам нужно создать способ создания новых файлов по имени! Я бы предложил цикл с парами счетчиков:

outer_count = 1
line_count = 0
sorting = True
while sorting:
    count = 0
    increment = (outer_count-1) * 300
    left = len(hold_lines) - increment
    file_name = "small_file_" + str(outer_count * 300) + ".txt"

В-третьих, внутри этого цикла вам понадобятся несколько вложенных циклов, которые сохранят правильные строки в массиве:

hold_new_lines = []
    if left < 300:
        while count < left:
            hold_new_lines.append(hold_lines[line_count])
            count += 1
            line_count += 1
        sorting = False
    else:
        while count < 300:
            hold_new_lines.append(hold_lines[line_count])
            count += 1
            line_count += 1

Наконец, снова в первом цикле вам нужно написать новый файл и добавить свой последний счетчик, чтобы ваш цикл снова прошел и написал новый файл

outer_count += 1
with open(file_name,'w') as next_file:
    for row in hold_new_lines:
        next_file.write(row)

обратите внимание: если количество строк не делится на 300, последний файл будет иметь имя, которое не соответствует последней строке файла.

Важно понять, почему эти петли работают. Вы настроили его так, чтобы в следующем цикле имя файла, который вы записываете, изменяется, потому что у вас есть имя, зависящее от изменяющейся переменной. Это очень полезный скриптовый инструмент для доступа к файлам, открытия, записи, организации и т.д.

Если вы не могли следовать тому, что было в этом цикле, вот полнота функции:

my_file = 'really_big_file.txt'
sorting = True
hold_lines = []
with open(my_file,'r') as text_file:
    for row in text_file:
        hold_lines.append(row)
outer_count = 1
line_count = 0
while sorting:
    count = 0
    increment = (outer_count-1) * 300
    left = len(hold_lines) - increment
    file_name = "small_file_" + str(outer_count * 300) + ".txt"
    hold_new_lines = []
    if left < 300:
        while count < left:
            hold_new_lines.append(hold_lines[line_count])
            count += 1
            line_count += 1
        sorting = False
    else:
        while count < 300:
            hold_new_lines.append(hold_lines[line_count])
            count += 1
            line_count += 1
    outer_count += 1
    with open(file_name,'w') as next_file:
        for row in hold_new_lines:
            next_file.write(row)

Ответ 5

lines_per_file = 300  # Lines on each small file
lines = []  # Stores lines not yet written on a small file
lines_counter = 0  # Same as len(lines)
created_files = 0  # Counting how many small files have been created

with open('really_big_file.txt') as big_file:
    for line in big_file:  # Go throught the whole big file
        lines.append(line)
        lines_counter += 1
        if lines_counter == lines_per_file:
            idx = lines_per_file * (created_files + 1)
            with open('small_file_%s.txt' % idx, 'w') as small_file:
                # Write all lines on small file
                small_file.write('\n'.join(stored_lines))
            lines = []  # Reset variables
            lines_counter = 0
            created_files += 1  # One more small file has been created
    # After for-loop has finished
    if lines_counter:  # There are still some lines not written on a file?
        idx = lines_per_file * (created_files + 1)
        with open('small_file_%s.txt' % idx, 'w') as small_file:
            # Write them on a last small file
            small_file.write('n'.join(stored_lines))
        created_files += 1

print '%s small files (with %s lines each) were created.' % (created_files,
                                                             lines_per_file)