Wie verkette ich Textdateien in Python?

Lesezeit: 7 Minuten

Benutzer-Avatar
JJ Beck

Ich habe eine Liste mit 20 Dateinamen, wie z ['file1.txt', 'file2.txt', ...]. Ich möchte ein Python-Skript schreiben, um diese Dateien zu einer neuen Datei zu verketten. Ich konnte jede Datei öffnen f = open(...)Zeile für Zeile durch Aufrufen lesen f.readline(), und schreiben Sie jede Zeile in diese neue Datei. Es erscheint mir nicht sehr “elegant”, besonders der Teil, wo ich Zeile für Zeile lesen/schreiben muss.

Gibt es eine “elegantere” Möglichkeit, dies in Python zu tun?

  • Es ist nicht Python, aber in Shell-Skripten könnten Sie so etwas tun cat file1.txt file2.txt file3.txt ... > output.txt. In Python, wenn Sie nicht mögen readline()es gibt immer readlines() oder einfach read().

    – jedwards

    28. November 2012 um 19:57 Uhr


  • @jedwards führe einfach die cat file1.txt file2.txt file3.txt Befehl verwenden subprocess Modul und fertig. Aber ich bin mir nicht sicher, ob cat funktioniert in Fenstern.

    – Ashwini Chaudhary

    28. November 2012 um 19:59 Uhr

  • Als Anmerkung, die Art und Weise, wie Sie beschreiben, ist eine schreckliche Art, eine Datei zu lesen. Verwenden Sie die with -Anweisung, um sicherzustellen, dass Ihre Dateien ordnungsgemäß geschlossen werden, und iterieren Sie über die Datei, um Zeilen abzurufen, anstatt sie zu verwenden f.readline().

    – Gareth Latty

    28. November 2012 um 20:04 Uhr

  • @jedwards cat funktioniert nicht, wenn die Textdatei Unicode ist.

    – Avi Cohen

    8. August 2013 um 12:11 Uhr

  • Aktuelle Analyse waymoot.org/home/python_string

    – Nu Everest

    9. Februar 2016 um 20:40 Uhr

Benutzer-Avatar
InspektorG4dget

Dies sollte es tun

Für große Dateien:

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)

Für kleine Dateien:

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

… und noch eine interessante, die mir eingefallen ist:

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)

Leider hinterlässt diese letzte Methode einige offene Dateideskriptoren, um die sich der GC sowieso kümmern sollte. Ich fand es einfach interessant

  • Dies wird bei großen Dateien sehr speicherineffizient sein.

    – Gareth Latty

    28. November 2012 um 20:06 Uhr


  • was erwägen wir a groß Datei sein?

    – Dee

    2. August 2015 um 22:55 Uhr

  • @dee: Eine Datei, die so groß ist, dass ihr Inhalt nicht in den Hauptspeicher passt

    – InspektorG4dget

    2. August 2015 um 22:59 Uhr

  • Warum würdest du das Ganze decodieren und neu codieren? und suchen Sie nach Zeilenumbrüchen und all dem unnötigen Zeug, wenn alles, was erforderlich ist, die Dateien verketten. das shutil.copyfileobj Antwort unten wird viel schneller sein.

    – fliegende Schafe

    19. August 2015 um 10:12 Uhr

  • Nur um es noch einmal zu wiederholen: Dies ist die falsche Antwort, shutdownil.copyfileobj ist die richtige Antwort.

    – Paul Croley

    5. April 2017 um 17:05 Uhr

Benutzer-Avatar
Miau

Verwenden shutil.copyfileobj.

Es liest die Eingabedateien automatisch Stück für Stück für Sie, was effizienter ist und die Eingabedateien einliest und auch dann funktioniert, wenn einige der Eingabedateien zu groß sind, um in den Speicher zu passen:

import shutil

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)

  • for i in glob.glob(r'c:/Users/Desktop/folder/putty/*.txt'): Nun, ich habe die for-Anweisung ersetzt, um alle Dateien im Verzeichnis außer my einzuschließen output_file fing an, in sehr kurzer Zeit wirklich riesig zu werden, wie in 100 GB.

    – R__raki__

    5. Oktober 2016 um 8:32 Uhr

  • Beachten Sie, dass die letzten Zeichenfolgen jeder Datei mit den ersten Zeichenfolgen der nächsten Datei zusammengeführt werden, wenn keine EOL-Zeichen vorhanden sind. In meinem Fall habe ich nach der Verwendung dieses Codes ein völlig beschädigtes Ergebnis erhalten. Ich habe wfd.write(b”\n”) nach copyfileobj hinzugefügt, um ein normales Ergebnis zu erhalten

    – Thelambofgoat

    18. Februar 2019 um 11:25 Uhr


  • @Thelambofgoat Ich würde sagen, das ist in diesem Fall keine reine Verkettung, aber hey, was auch immer Ihren Bedürfnissen entspricht.

    – Hallo Auf Wiedersehen

    18. Oktober 2019 um 8:31 Uhr

  • Das ist mit Abstand die beste Antwort!

    – Kai Petzke

    14. August 2020 um 18:30 Uhr

  • Das geht super schnell und wie ich es benötigt habe. ja, es fügt keine neue Zeile zwischen “Zwei Dateien enden und starten” hinzu und genau das habe ich gebraucht. Also nicht aktualisieren 😀

    – Adnan Ali

    22. Februar 2021 um 9:11 Uhr

Benutzer-Avatar
Abart

Genau das ist es Dateieingabe ist für:

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

Für diesen Anwendungsfall ist es wirklich nicht viel einfacher, als nur manuell über die Dateien zu iterieren, aber in anderen Fällen ist es sehr praktisch, einen einzigen Iterator zu haben, der über alle Dateien iteriert, als wären sie eine einzelne Datei. (Auch die Tatsache, dass fileinput schließt jede Datei, sobald sie fertig ist, bedeutet, dass dies nicht erforderlich ist with oder close jeder, aber das ist nur eine Einsparung von einer Zeile, keine so große Sache.)

Es gibt einige andere raffinierte Funktionen fileinputwie die Möglichkeit, Dateien direkt zu ändern, indem Sie jede Zeile filtern.


Wie in den Kommentaren erwähnt und in einem anderen Beitrag besprochen, fileinput für Python 2.7 funktioniert nicht wie angegeben. Hier leichte Modifikation, um den Code Python 2.7-konform zu machen

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

  • @Lattyware: Ich denke, die meisten Leute, die davon erfahren fileinput gesagt, dass es eine Möglichkeit ist, eine einfache zu drehen sys.argv (oder was als Argumente danach übrig bleibt optparse/etc.) in eine große virtuelle Datei für triviale Skripte und denken Sie nicht daran, sie für etwas anderes zu verwenden (dh wenn die Liste keine Befehlszeilenargumente ist). Oder sie lernen, aber dann vergessen – ich entdecke es alle ein, zwei Jahre wieder …

    – Abart

    28. November 2012 um 20:24 Uhr


  • @abament glaube ich for line in fileinput.input() ist in diesem speziellen Fall nicht der beste Weg: Das OP möchte Dateien verketten und nicht Zeile für Zeile lesen, was theoretisch ein längerer Prozess zur Ausführung ist

    – eyquem

    28. November 2012 um 20:30 Uhr

  • @eyquem: Es ist kein längerer Prozess zum Ausführen. Wie Sie selbst betont haben, lesen zeilenbasierte Lösungen nicht jeweils ein Zeichen. Sie lesen Chunks ein und ziehen Zeilen aus einem Puffer. Die E/A-Zeit wird die Zeilenanalysezeit vollständig überschwemmen, solange der Implementierer also nichts schrecklich Dummes beim Puffern gemacht hat, wird es genauso schnell sein (und möglicherweise sogar schneller als der Versuch, einen guten Puffer zu erraten Größe selbst, wenn Sie denken, dass 10000 eine gute Wahl ist).

    – Abart

    28. November 2012 um 20:46 Uhr

  • @abarnert NEIN, 10000 ist keine gute Wahl. Es ist in der Tat eine sehr schlechte Wahl, weil es keine Zweierpotenz ist und lächerlich wenig groß ist. Bessere Größen wären 2097152 (221), 16777216 (224) oder sogar 134217728 (2**27) , warum nicht ?, 128 MB sind nichts in einem RAM von 4 GB.

    – eyquem

    28. November 2012 um 21:55 Uhr

  • Beispielcode nicht ganz gültig für Python 2.7.10 und höher: stackoverflow.com/questions/30835090/…

    – Cnrl

    25. September 2015 um 13:43 Uhr

Ich weiß nichts über Eleganz, aber das funktioniert:

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

Benutzer-Avatar
Hämmern

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

Ein einfacher Benchmark zeigt, dass Shutil besser abschneidet.

Benutzer-Avatar
Lukasg

Was ist falsch an UNIX-Befehlen? (vorausgesetzt, Sie arbeiten nicht unter Windows):

ls | xargs cat | tee output.txt erledigt den Job (Sie können es von Python mit Unterprozess aufrufen, wenn Sie möchten)

Eine Alternative zu @inspectorG4dget answer (beste Antwort bis dato 29.03.2016). Ich habe mit 3 Dateien von 436 MB getestet.

@inspectorG4dget Lösung: 162 Sekunden

Die folgende Lösung: 125 Sekunden

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

Die Idee ist, eine Batch-Datei zu erstellen und auszuführen, wobei “alte gute Technologie” genutzt wird. Es ist Semi-Python, arbeitet aber schneller. Funktioniert für Fenster.

1039460cookie-checkWie verkette ich Textdateien in Python?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy