2017-02-15 2 views
3

Ich habe 30 Textdateien von jeweils 30 Zeilen. Aus irgendeinem Grund muss ich ein Skript schreiben, das Datei 1 öffnet, Zeile 1 von Datei 1 druckt, es schließt, Datei 2 öffnet, Zeile 2 von Datei 2 druckt, schließt, und so weiter. Ich habe versucht, diese:Drucken bestimmte Zeilen mehrerer Dateien in Python

import glob 

files = glob.glob('/Users/path/to/*/files.txt')    
for file in files: 
    i = 0 
    while i < 30: 
     with open(file,'r') as f: 
      for index, line in enumerate(f): 
       if index == i: 
        print(line) 
        i += 1 
        f.close() 
      continue 

Offensichtlich bekam ich folgende Fehlermeldung:

Valueerror: I/O-Operation auf geschlossene Datei.

Wegen der Sache f.close(). Wie kann ich von einer Datei zur nächsten wechseln, nachdem ich nur die gewünschte Zeile gelesen habe?

+3

Sie können 'break' verwenden, um eine Schleife zu beenden; Ersetzen Sie 'f.close()' damit. Das "continue" am unteren Rand ist auch nicht notwendig, und die äußere Schleife kann ein 'für i im Bereich (0, 30) sein:' (oder 'i, file in enumerate (dateien)'?) Ohne "i" explizit zu inkrementieren . – Ryan

+1

Hinweis zu @Ryan: Das 'f.close()' wird überhaupt nicht benötigt, weil Sie die 'with' -Anweisung (korrekt) beim Öffnen der Datei (korrekt) verwendet haben und sicherstellen, dass sie beim Beenden automatisch geschlossen wird der Block. – ShadowRanger

+0

Randnotiz: Sie können die explizite innere Schleife vollständig mit 'itertools.islice' entfernen. Ersetzen Sie den gesamten Inhalt des 'with' -Blocks durch' print (next (itertools.islice (f, i, None)))}, es ist keine explizite Schleifenbildung erforderlich. Dies erfordert @ Ryans vorgeschlagene Änderung des Ersetzens der äußeren "while" -Schleife durch ein "für i, file in enumerate (dateien):" (oder um sicherzustellen, dass nur 30 Dateien verarbeitet werden, für i eine Datei in enumerate (islice (dateien), 30)): '), so dass Sie" i "nicht manuell verfolgen/inkrementieren. – ShadowRanger

Antwort

0

Ich denke, so etwas wie das ist, was Sie wollen:

import glob 

files = glob.glob('/Users/path/to/*/files.txt')    
for file in files: 
    i = 0 
    while i < 30: 
     with open(file,'r') as f: 
      for index, line in enumerate(f): 
       if index == i: 
        print(line) 
        i += 1 
        break 
     f.close() 

Aktuell Sie die Datei in der Mitte des for-Schleife schließen und dann erneut versuchen, es zu lesen in. Wenn Sie also die Datei nur schließen, wenn Sie nicht mehr in der for-Schleife sind, sollte es in Ordnung sein.

2

Sie können das linecache Modul die Linie, die Sie benötigen und sich eine Menge Kopfschmerzen sparen:

import glob 
import linecache 

line = 1 
for file in glob.glob('/Users/path/to/*/files.txt'): 
    print(linecache.getline(file, line)) 
    line += 1 
    if line > 30: # if you really need to limit it to only 30 
     break 
+1

Guter Vorschlag, obwohl ich feststellen werde, dass 'linecache' die gesamte Datei im Speicher zwischenspeichert, um eine einzige Zeile zu erhalten; Dies ist normalerweise kein Problem für kleinere Dateien (z. B. die Quelldateien, für die das Modul ursprünglich entworfen wurde), insbesondere wenn Sie einen wahlfreien Zugriff für mehrere Zeilen durchführen müssen, aber für beliebige Eingaben können Sie eine GB-Datei in den Speicher lesen (wo die Zeilen dank Python-Overhead weit mehr als eine GB Speicher benötigen, auch wenn alles, was Sie wollen, die erste Zeile der Datei ist. Es wäre auch sinnvoll, das manuelle "Zeilen" -Verfahren zu vermeiden und den Aufruf "glob" nur in "enumerate" zu verpacken. – ShadowRanger

+0

Wahr, während sehr bequem 'linecache' kann Speicher auffressen, aber ich habe nicht die Vorstellung, dass OP große Dateien haben wird, um damit umzugehen. Man kann immer 'clearcache()' aufrufen, nachdem man sich damit beschäftigt hat, wenn der Zugriff auf die Dateien nicht mehr benötigt wird. Und wenn der Zugriff auf wirklich große Dateien erforderlich ist, würde das durchgehen Zeile für Zeile (die traditionelle Art) wahrscheinlich schreckliche Leistung entweder - wenn das die Voraussetzung wäre, würde ich eher vorschlagen, verwenden Sie das "mmap" -Modul und lassen Sie das Betriebssystem den Zugriff optimieren zu den Daten. – zwer

+0

Danke! Das hat perfekt funktioniert, obwohl ich "Zeile 0" durch "Zeile 1" ersetzen musste. –

6

Zunächst einmal, die Frage zu beantworten, wie es in den Kommentaren erwähnt, Ihr Hauptproblem ist, dass Sie schließen die Datei und versuchen dann, sie weiter zu durchlaufen. Der Schuldige Code:

 for index, line in enumerate(f): # <-- Reads 
      if index == i: 
       print(line) 
       i += 1 
       f.close()    # <-- Closes when you get a hit 
             # But loop is not terminated, so you'll loop again 

Die einfachste Lösung ist nur break statt explizit zu schließen, da Ihre with Aussage bereits determinis Schließung gewährleistet, wenn der Block verlassen wird:

 for index, line in enumerate(f): 
      if index == i: 
       print(line) 
       i += 1 
       break 

Aber weil dieser Spaß war, Hier ist ein wesentlich aufgeräumter Code, um die gleiche Aufgabe zu erledigen:

import glob 
from itertools import islice 

# May as well use iglob since we'll stop processing at 30 files anyway  
files = glob.iglob('/Users/path/to/*/files.txt') 

# Stop after no more than 30 files, use enumerate to track file num 
for i, file in enumerate(islice(files, 30)): 
    with open(file,'r') as f: 
     # Skip the first i lines of the file, then print the next line 
     print(next(islice(f, i, None))) 
+0

Vielen Dank für die Erklärungen und den Code! –

0

Teilen Sie Ihren Job in einfachere Schritte, bis der letzte Schritt trivial ist. Verwenden Sie Funktionen.

Denken Sie daran, dass ein Dateiobjekt als eine Sequenz von Zeilen funktioniert.

def nth(n, sequence): 
    for position, item in enumerate(sequence): 
    if position == n: 
     return item 
    return None # if the sequence ended before position n 

def printNthLines(glob_pattern) 
    # Note: sort file names; glob guarantees no order. 
    filenames = sorted(glob.glob(glob_pattern)) 
    for position, filename in enumerate(filenames): 
    with open(filename) as f: 
     line = nth(position, f) # Pick the n-th line. 
     if line is not None: 
     print(line) 
     # IDK what to do if there's no n-th line in n-th file 

printNthLines('path/to/*/file.txt') 

Offensichtlich wir scannen n-te Datei zu n-ten Zeile, aber das ist unvermeidlich, es gibt keine Möglichkeit, direkt an n-ten Zeile in einer Klartextdatei zu erhalten.

Verwandte Themen