Текстовые файлы для конкатенации Python

У меня есть список из 20 имен файлов, например ['file1.txt', 'file2.txt', ...]. Я хочу написать Python script, чтобы объединить эти файлы в новый файл. Я могу открыть каждый файл с помощью f = open(...), читать строки за строкой, вызывая f.readline() и записывать каждую строку в этот новый файл. Мне это не кажется очень "изящным", особенно тем, что мне нужно читать//писать по строкам.

Есть ли более "элегантный" способ сделать это в Python?

Ответ 1

Это должно сделать это

Для больших файлов:

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for fname in filenames:
        with open(fname) as infile:
            for line in infile:
                outfile.write(line)

Для небольших файлов:

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for fname in filenames:
        with open(fname) as infile:
            outfile.write(infile.read())

... и еще один интересный, который я думал о:

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for line in itertools.chain.from_iterable(itertools.imap(open, filnames)):
        outfile.write(line)

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

Ответ 2

Используйте shutil.copyfileobj.

Он автоматически считывает входные файлы по частям для вас, что более эффективно и считывает входные файлы и будет работать, даже если некоторые из входных файлов слишком велики для размещения в памяти:

with open('output_file.txt','wb') as wfd:
    for f in ['seg1.txt','seg2.txt','seg3.txt']:
        with open(f,'rb') as fd:
            shutil.copyfileobj(fd, wfd)

Ответ 3

То, что fileinput для:

import fileinput
with open(outfilename, 'w') as fout, fileinput.input(filenames) as fin:
    for line in fin:
        fout.write(line)

Для этого варианта использования это действительно не намного проще, чем просто перебирать файлы вручную, но в других случаях очень удобно использовать один итератор, который выполняет итерацию по всем файлам, как если бы они были одним файлом. (Кроме того, тот факт, что fileinput закрывает каждый файл, как только это делается, означает, что нет необходимости в with или close для каждого из них, но это только однострочная экономия, а не большая сделка.)

В fileinput есть еще несколько отличных функций, например, возможность делать модификации файлов на месте только путем фильтрации каждой строки.


Как отмечено в комментариях и обсуждается в другом сообщении , fileinput для Python 2.7 не будет работать, как указано. Здесь небольшая модификация, чтобы сделать код Python 2.7 совместимым

with open('outfilename', 'w') as fout:
    fin = fileinput.input(filenames)
    for line in fin:
        fout.write(line)
    fin.close()

Ответ 4

Я не знаю об элегантности, но это работает:

    import glob
    import os
    for f in glob.glob("file*.txt"):
         os.system("cat "+f+" >> OutFile.txt")

Ответ 5

Что не так с командами UNIX? (если вы не работаете в Windows):

ls | xargs cat | tee output.txt выполняет задание (вы можете вызвать его из python с помощью подпроцесса, если хотите)

Ответ 6

Проверьте метод .read() объекта File:

http://docs.python.org/2/tutorial/inputoutput.html#methods-of-file-objects

Вы можете сделать что-то вроде:

concat = ""
for file in files:
    concat += open(file).read()

или более "элегантный" python-way:

concat = ''.join([open(f).read() for f in files])

который, согласно этой статье: http://www.skymind.com/~ocrow/python_string/ также будет самым быстрым.

Ответ 7

Альтернатива ответу @inspectorG4dget (лучший ответ на сегодняшний день 29-03-2016). Я тестировал 3 файла из 436 МБ.

@inspectorG4dget решение: 162 секунды

Следующее решение: 125 секунд

from subprocess import Popen
filenames = ['file1.txt', 'file2.txt', 'file3.txt']
fbatch = open('batch.bat','w')
str ="type "
for f in filenames:
    str+= f + " "
fbatch.write(str + " > file4results.txt")
fbatch.close()
p = Popen("batch.bat", cwd=r"Drive:\Path\to\folder")
stdout, stderr = p.communicate()

Идея состоит в том, чтобы создать пакетный файл и выполнить его, воспользовавшись "старыми хорошими технологиями". Его полупиттон работает быстрее. Работает для окон.

Ответ 8

Если у вас много файлов в каталоге, тогда glob2 может быть лучшим вариантом для создания списка имен файлов, а не для записи их вручную.

import glob2

filenames = glob2.glob('*.txt')  # list of all .txt files in the directory

with open('outfile.txt', 'w') as f:
    for file in filenames:
        with open(file) as infile:
            f.write(infile.read()+'\n')

Ответ 9

outfile.write(infile.read()) # time: 2.1085190773010254s
shutil.copyfileobj(fd, wfd, 1024*1024*10) # time: 0.60599684715271s

Простой тест показывает, что шутил работает лучше.

Ответ 10

Если файлы не являются гигантскими:

with open('newfile.txt','wb') as newf:
    for filename in list_of_files:
        with open(filename,'rb') as hf:
            newf.write(hf.read())
            # newf.write('\n\n\n')   if you want to introduce
            # some blank lines between the contents of the copied files

Если файлы слишком велики, чтобы их можно было полностью читать и хранить в ОЗУ, алгоритм должен быть немного отличающимся для чтения каждого файла, который должен быть скопирован в цикле кусками фиксированной длины, например, с помощью read(10000).

Ответ 11

def concatFiles():
    path = 'input/'
    files = os.listdir(path)
    for idx, infile in enumerate(files):
        print ("File #" + str(idx) + "  " + infile)
    concat = ''.join([open(path + f).read() for f in files])
    with open("output_concatFile.txt", "w") as fo:
        fo.write(path + concat)

if __name__ == "__main__":
    concatFiles()

Ответ 12

Это представление Python 3.

from pathlib import Path

filenames = ['file1.txt', 'file2.txt', ...]
output_file = Path('output_file.txt')
for file in file_list:
    output_file.write_text(Path(file).read_text())