189 votes

Python concaténation de fichiers texte

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

Existe-t-il un moyen plus "élégant" de faire cela en Python ?

8 votes

Ce n'est pas python, mais en script shell vous pourriez faire quelque chose comme cat file1.txt file2.txt file3.txt ... > output.txt . En python, si vous n'aimez pas readline() il y a toujours readlines() ou simplement read() .

1 votes

@jedwards exécutez simplement le cat file1.txt file2.txt file3.txt en utilisant la commande subprocess et vous avez terminé. Mais je ne suis pas sûr que cat fonctionne sous Windows.

5 votes

À titre d'information, la manière que vous décrivez est une manière terrible de lire un fichier. Utilisez la fonction with pour s'assurer que vos fichiers sont fermés correctement, et itérer sur le fichier pour obtenir des lignes, plutôt que d'utiliser l'instruction f.readline() .

293voto

inspectorG4dget Points 25092

Cela devrait le faire

Pour les fichiers volumineux :

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, dont le GC devrait s'occuper de toute façon. Je pensais juste que c'était intéressant

11 votes

Pour les gros fichiers, cette méthode est très peu efficace en termes de mémoire.

0 votes

@inspectorG4dget Je ne pense pas que ce code soit très efficace en termes de temps pour les fichiers qui ne sont pas volumineux et qui peuvent être lus entièrement en une fois. À mon avis, il est impossible d'écrire un code qui soit aussi efficace pour les gros fichiers que pour les fichiers moins gros.

0 votes

@eyquem : Avez-vous réellement effectué des tests de performance ou un profilage sur l'une de ces solutions, ou devinez-vous simplement ce qui va être rapide en vous basant sur vos intuitions sur le fonctionnement des ordinateurs ?

64voto

abarnert Points 94246

C'est exactement ce que entrée du fichier 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 pas vraiment plus simple que d'itérer sur les fichiers manuellement, mais dans d'autres cas, avoir un seul itérateur qui itère sur tous les fichiers comme s'ils étaient un seul fichier est très pratique. (De plus, le fait que fileinput ferme chaque fichier dès qu'il est terminé, ce qui signifie qu'il n'y a pas besoin de with ou close chacun, mais ce n'est qu'une économie d'une ligne, ce n'est pas si grave).

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


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

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

0 votes

@Lattyware : Je pense que la plupart des gens qui apprennent sur fileinput on leur dit que c'est un moyen de transformer une simple sys.argv (ou ce qu'il reste comme args après que optparse /etc.) en un grand fichier virtuel pour des scripts triviaux, et ne pensent pas à l'utiliser pour autre chose (c'est-à-dire, quand la liste n'est pas des args de ligne de commande). Ou bien ils apprennent, mais oublient ensuite - je continue à le redécouvrir tous les ans ou tous les deux ans

1 votes

@abament je pense for line in fileinput.input() n'est pas la meilleure solution dans ce cas particulier : le PO veut concaténer des fichiers, et non les lire ligne par ligne, ce qui est un processus théoriquement plus long à exécuter.

1 votes

@eyquem : Ce n'est pas un processus plus long à exécuter. Comme vous l'avez souligné vous-même, les solutions basées sur les lignes ne lisent pas un caractère à la fois ; elles lisent par morceaux et extraient les lignes d'un tampon. Le temps d'entrée/sortie va complètement écraser le temps de lecture des lignes, donc tant que l'implémenteur n'a pas fait quelque chose d'horriblement stupide dans la mise en mémoire tampon, ce sera tout aussi rapide (et peut-être même plus rapide que d'essayer de deviner une bonne taille de tampon vous-même, si vous pensez que 10000 est un bon choix).

8voto

Daniel Points 21

Je ne sais pas pour l'élégance, mais ça marche :

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

9 votes

Vous pouvez même éviter la boucle : import os ; os.system("cat file*.txt >> OutFile.txt")

12 votes

N'est pas multiplateforme et sera interrompu pour les noms de fichiers contenant des espaces

4 votes

Ce n'est pas sûr ; aussi, cat peut prendre une liste de fichiers, donc pas besoin de l'appeler à plusieurs reprises. Vous pouvez facilement le rendre sûr en appelant subprocess.check_call au lieu de os.system

5voto

georgesl Points 4116

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 depuis python avec subprocess si vous le souhaitez)

26 votes

Parce que c'est une question sur Python.

2 votes

Rien de mal en général, mais cette réponse est cassée (ne pas passer la sortie de ls à xargs, juste passer la liste des fichiers à cat directement : cat * | tee output.txt ).

0 votes

S'il peut également insérer le nom du fichier, ce serait formidable.

1voto

Alex Kawrykow Points 16

Vérifiez 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 méthode plus "élégante" en 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.

10 votes

Cela produira une chaîne géante qui, selon la taille des fichiers, pourrait être plus grande que la mémoire disponible. Comme Python fournit un accès paresseux aux fichiers, c'est une mauvaise idée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X