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

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

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

11 ответов


это должно помочь

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

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 должен позаботиться в любом случае. Я просто подумал, что это интересно


использовать 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, 1024*1024*10)
            #10MB per writing chunk to avoid reading big file into memory.

именно так 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()

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

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

что не так с командами UNIX ? (учитывая, что вы не работаете в Windows):

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


Если файлы не гигантские:

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) например.


Если у вас много файлов в каталоге, тут 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')

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

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


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

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

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

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

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

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

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


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()

альтернатива @inspectorG4dget answer (лучший ответ на дату 29-03-2016). Я тестировал с 3 файлами 436MB.

@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()

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