Python script, чтобы объединить все файлы в каталоге в один файл

Я написал следующий script, чтобы объединить все файлы в каталоге в один файл.

Можно ли это оптимизировать с точки зрения

  • idiomatic python

  • время

Вот фрагмент:

import time, glob

outfilename = 'all_' + str((int(time.time()))) + ".txt"

filenames = glob.glob('*.txt')

with open(outfilename, 'wb') as outfile:
    for fname in filenames:
        with open(fname, 'r') as readfile:
            infile = readfile.read()
            for line in infile:
                outfile.write(line)
            outfile.write("\n\n")

Ответ 1

Используйте shutil.copyfileobj для копирования данных:

import shutil

with open(outfilename, 'wb') as outfile:
    for filename in glob.glob('*.txt'):
        if filename == outfilename:
            # don't want to copy the output into the output
            continue
        with open(filename, 'rb') as readfile:
            shutil.copyfileobj(readfile, outfile)

shutil читает из объекта readfile в кусках, записывая их непосредственно в файл файл outfile. Не используйте readline() или итерационный буфер, так как вам не нужны накладные расходы на поиск окончаний строк.

Использовать тот же режим для чтения и записи; это особенно важно при использовании Python 3; Я использовал двоичный режим для обоих.

Ответ 2

Используя Python 2.7, я проверил некоторые "тесты"

outfile.write(infile.read())

против

shutil.copyfileobj(readfile, outfile)

Я повторил более 20 файлов .txt размером от 63 МБ до 313 МБ с общим размером файла ~ 2,6 ГБ. В обоих методах нормальный режим чтения выполнялся лучше, чем двоичный режим чтения, а shutil.copyfileobj был обычно быстрее, чем outfile.write.

При сравнении наихудшей комбинации (outfile.write, двоичный режим) с наилучшей комбинацией (shutil.copyfileobj, нормальный режим чтения) разница была довольно значительной:

outfile.write, binary mode: 43 seconds, on average.

shutil.copyfileobj, normal mode: 27 seconds, on average.

Исходный файл имел окончательный размер 2620 МБ в режиме нормального чтения и 2578 МБ в режиме двоичного чтения.

Ответ 3

Не нужно использовать много переменных.

with open(outfilename, 'w') as outfile:
    for fname in filenames:
        with open(fname, 'r') as readfile:
            outfile.write(readfile.read() + "\n\n")

Ответ 4

Модуль fileinput обеспечивает естественный способ перебора нескольких файлов

for line in fileinput.input(glob.glob("*.txt")):
    outfile.write(line)

Ответ 5

Мне было любопытно узнать больше о производительности, и я использовал ответы Мартина Питерса и Стивена Миллера.

Я пробовал двоичный и текстовый режимы с shutil и без shutil. Я попытался объединить 270 файлов.

Текстовый режим -

def using_shutil_text(outfilename):
    with open(outfilename, 'w') as outfile:
        for filename in glob.glob('*.txt'):
            if filename == outfilename:
                # don't want to copy the output into the output
                continue
            with open(filename, 'r') as readfile:
                shutil.copyfileobj(readfile, outfile)

def without_shutil_text(outfilename):
    with open(outfilename, 'w') as outfile:
        for filename in glob.glob('*.txt'):
            if filename == outfilename:
                # don't want to copy the output into the output
                continue
            with open(filename, 'r') as readfile:
                outfile.write(readfile.read())

Бинарный режим -

def using_shutil_text(outfilename):
    with open(outfilename, 'wb') as outfile:
        for filename in glob.glob('*.txt'):
            if filename == outfilename:
                # don't want to copy the output into the output
                continue
            with open(filename, 'rb') as readfile:
                shutil.copyfileobj(readfile, outfile)

def without_shutil_text(outfilename):
    with open(outfilename, 'wb') as outfile:
        for filename in glob.glob('*.txt'):
            if filename == outfilename:
                # don't want to copy the output into the output
                continue
            with open(filename, 'rb') as readfile:
                outfile.write(readfile.read())

Время работы в двоичном режиме -

Shutil - 20.161773920059204
Normal - 17.327500820159912

Время работы в текстовом режиме -

Shutil - 20.47757601737976
Normal - 13.718038082122803

Похоже, в обоих режимах shutil работает одинаково, а текстовый режим быстрее двоичного.

ОС: Mac OS 10.14 Мохаве. Macbook Air 2017.

Ответ 6

Вы можете напрямую перебирать строки файлового объекта, не читая все это в памяти:

with open(fname, 'r') as readfile:
    for line in readfile:
        outfile.write(line)