Python concatenar archivos de texto

Tengo una lista de 20 nombres de archivos, como ['file1.txt', 'file2.txt', ...] . Quiero escribir un script en Python para concatenar estos archivos en un nuevo archivo. Podría abrir cada archivo por f = open(...) , leer línea por línea llamando a f.readline() , y escribir cada línea en ese nuevo archivo. No me parece muy “elegante”, especialmente la parte en la que tengo que leer // escribir línea por línea.

¿Hay una forma más “elegante” de hacer esto en Python?

Esto deberia hacerlo

Para archivos grandes:

 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) 

Para archivos pequeños:

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

… y otro interesante que pensé :

 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) 

Lamentablemente, este último método deja algunos descriptores de archivos abiertos, que el GC debería cuidar de todos modos. Solo pensé que era interesante

Utilice shutil.copyfileobj .

Lee automáticamente los archivos de entrada parte por parte, lo que es más eficiente, leer los archivos de entrada y funcionará incluso si algunos de los archivos de entrada son demasiado grandes para caber en la memoria:

 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) 

Eso es exactamente lo que la entrada de archivo es para:

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

Para este caso de uso, realmente no es mucho más simple que simplemente iterar sobre los archivos manualmente, pero en otros casos, tener un solo iterador que itera sobre todos los archivos como si fueran un solo archivo es muy útil. (Además, el hecho de que la fileinput archivo cierre cada archivo tan pronto como se hace significa que no hay necesidad de close o close cada uno, pero eso es solo un ahorro de una línea, no es un gran problema).

Hay algunas otras características ingeniosas en la entrada de fileinput , como la capacidad de realizar modificaciones en el lugar de los archivos con solo filtrar cada línea.


Como se señaló en los comentarios y se discutió en otra publicación , la fileinput de fileinput para Python 2.7 no funcionará como se indica. Aquí leve modificación para hacer que el código cumpla con Python 2.7.

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

No sé de elegancia, pero esto funciona:

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

¿Qué hay de malo con los comandos de UNIX? (Dado que no estás trabajando en Windows):

ls | xargs cat | tee output.txt ls | xargs cat | tee output.txt hace el trabajo (puede llamarlo desde Python con subproceso si lo desea)

Revisa el método .read () del objeto File:

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

Podrías hacer algo como:

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

o una forma de python más ‘elegante’:

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

que, según este artículo: http://www.skymind.com/~ocrow/python_string/ también sería el más rápido.

Una alternativa a la respuesta @ inspectorG4dget (la mejor respuesta hasta la fecha 29-03-2016). He probado con 3 archivos de 436MB.

Solución @ inspectorG4dget: 162 segundos

La siguiente solución: 125 segundos.

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

La idea es crear un archivo por lotes y ejecutarlo, aprovechando la “tecnología antigua”. Es semi-python pero trabaja más rápido. Obras para ventanas.

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

Un simple punto de referencia muestra que el shutil funciona mejor.

Si los archivos no son gigantescos:

 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 

Si los archivos son demasiado grandes para ser completamente leídos y guardados en la RAM, el algoritmo debe ser un poco diferente para leer cada archivo que se va a copiar en un bucle por trozos de longitud fija, por ejemplo, read(10000) .

Si tiene muchos archivos en el directorio, glob2 podría ser una mejor opción para generar una lista de nombres de archivos en lugar de escribirlos a mano.

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

Esta es una representación de 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())