web-dev-qa-db-fra.com

Python concaténer des fichiers texte

J'ai une liste de 20 noms de fichiers, comme ['file1.txt', 'file2.txt', ...]. Je veux écrire un script Python pour concaténer ces fichiers dans un nouveau fichier. Je pourrais ouvrir chaque fichier avec f = open(...), lire ligne par ligne en appelant f.readline() et écrire chaque ligne dans ce nouveau fichier. Cela ne me semble pas très "élégant", en particulier la partie où je dois lire // écrire ligne par ligne.

Existe-t-il une manière plus "élégante" de faire cela en Python?

146
JJ Beck

Cela devrait le faire

Pour les gros fichiers:

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)

Pour les petits fichiers:

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

… et un autre intéressant auquel j'ai 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)

Malheureusement, cette dernière méthode laisse quelques descripteurs de fichiers ouverts, que le GC devrait s’occuper de toute façon. Je pensais juste que c'était intéressant

225
inspectorG4dget

Utilisez shutil.copyfileobj.

Il lit automatiquement les morceaux d'entrée par morceau pour vous, ce qui est plus efficace et permet de lire les fichiers d'entrée et fonctionne même si certains des fichiers d'entrée sont trop volumineux pour tenir dans la mémoire:

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)
158
Meow

C'est exactement ce que fileinput est pour:

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

Pour ce cas d'utilisation, ce n'est vraiment pas plus simple que de simplement parcourir les fichiers manuellement, mais dans d'autres cas, avoir un seul itérateur qui itère sur tous les fichiers comme s'il s'agissait d'un seul fichier est très pratique. (De plus, le fait que fileinput ferme chaque fichier dès qu'il est terminé signifie qu'il n'est pas nécessaire de with ou close chacun, mais ce n'est qu'une économie d'une ligne, pas si grande d'un accord.)

Il existe d'autres fonctionnalités intéressantes dans fileinput, comme la possibilité d'effectuer des modifications sur place de fichiers en filtrant chaque ligne.


Comme indiqué dans les commentaires et discuté dans un autre post , fileinput pour Python 2.7 ne fonctionnera pas comme indiqué. Voici une légère modification pour rendre le code Python 2.7 conforme

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

Je ne sais pas à propos de l'élégance, mais cela fonctionne:

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

Quel est le problème avec les commandes UNIX? (étant donné que vous ne travaillez pas sous Windows):

ls | xargs cat | tee output.txt fait le travail (vous pouvez l'appeler de python avec un sous-processus si vous le souhaitez)

6
lucasg

Une alternative à la réponse @ inspectorG4dget (meilleure réponse à ce jour du 29-03-2016). J'ai testé avec 3 fichiers de 436MB.

@ inspectorG4dget solution: 162 secondes

La solution suivante: 125 secondes

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

L'idée est de créer un fichier de commandes et de l'exécuter, en tirant parti de la "vieille technologie". Son semi-python mais travaille plus vite. Fonctionne pour les fenêtres.

2
João Palma

Découvrez la méthode .read () de l'objet File:

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

Vous pourriez faire quelque chose comme:

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

ou une façon plus "élégante" de python:

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

qui, selon cet article: http://www.skymind.com/~ocrow/python_string/ serait également le plus rapide.

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

Un simple point de repère montre que le shutil fonctionne mieux.

2
haoming

Si vous avez beaucoup de fichiers dans le répertoire, alors glob2 pourrait être une meilleure option pour générer une liste de noms de fichiers plutôt que de les écrire à la main.

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')
2
Sharad

Si les fichiers ne sont pas gigantesques:

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 les fichiers sont trop volumineux pour être entièrement lus et conservés en RAM, l'algorithme doit être un peu différent pour lire chaque fichier à copier en boucle par fragments de longueur fixe, en utilisant read(10000) par exemple.

1
eyquem
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 __== "__main__":
    concatFiles()
0
user2825287

Ceci est une représentation 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())
0
Back2Basics