Reputation: 51
I have 30 text files of 30 lines each. For some reason, I need to write a script that opens file 1, prints line 1 of file 1, closes it, opens file 2, prints line 2 of file 2, closes it, and so on. I tried this:
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
Obviously, I got the following error:
ValueError: I/O operation on closed file.
Because of the f.close() thing. How can I do to move from a file to the next one after reading only the desired line?
Upvotes: 3
Views: 2187
Reputation: 40894
Split your job into simpler steps, until the final step is trivial. Use functions.
Remember that a file object works as a sequence of lines.
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')
Obviously we scan n-th file to n-th line, but this is inevitable, there's no way to get directly to n-th line in a plaintext file.
Upvotes: 0
Reputation: 25789
You can use the linecache
module to get the line you need and save yourself a lot of headache:
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
Upvotes: 2
Reputation: 155418
First off, to answer the question, as noted in the comments, your main problem is that you close the file then try to continue iterating it. The guilty 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
The simplest fix is to just break
instead of explicitly closing, since your with
statement already guarantees deterministic closing when the block is exited:
for index, line in enumerate(f):
if index == i:
print(line)
i += 1
break
But because this was fun, here's a significantly cleaned up bit of code to accomplish the same task:
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)))
Upvotes: 6
Reputation: 80
I think something like this is what you want:
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()
Currently you are closing the file in the middle of the for loop and then trying to read it in again. So if you only close the file once you are out of the for loop it should be ok.
Upvotes: 0