Reputation: 61
Here is this block of code I'm trying to finish:
elif parameter == 'statistics':
outfile.write(stats(infile))
for line in infile:
outfile.write(line)
So essentially, I am trying to write the statistics of the file into the new file that is being copied. The statistics works and everything as when I open the file, the statistics are written in. However, I noticed because of the two outfile.write it seems to close after the first one, so only the statistics go in and not the rest of the content in the original file.
The error that I am getting is this:
ValueError: I/O operation on closed file.
I am unsure why the file is closing.
EDIT: Here is the whole code, as requested
def copy_file():
infile_name = input("Please enter the name of the file to copy: ")
infile = open(infile_name, 'r', encoding='utf8')
parameter = input("Please enter a parameter(line numbers, Gutenberg trim, statistics, none): ")
outfile_name = input("Please enter the name of the new copy: ")
outfile = open(outfile_name, 'w', encoding='utf8')
counter = 1
if parameter == 'line numbers':
for line in infile:
outfile.write(f' {counter:6}: {line}')
counter += 1
elif parameter == 'Gutenberg trim':
copyStart = False
for line in infile:
#print(line.strip())
if '*** START' in line.strip():
copyStart = True
continue
elif '*** END' in line.strip():
copyStart = False
break
if copyStart == True:
outfile.write(line)
elif parameter == 'statistics':
outfile.write(stats(infile))
for line in infile:
outfile.write(line)
else:
for line in infile:
outfile.write(line)
infile.close()
outfile.close()
copy_file()
EDIT2: So sorry for not including it. Here is the stats function:
def stats(text) -> str:
with text as infile:
totallines = 0
emplines = 0
characters = 0
for line in infile:
totallines += 1
characters += len(line)
if len(line.strip()) == 0:
emplines += 1
lines = totallines - emplines
totalaveChars = characters/totallines
nonempaveChars = characters/lines
result = (f'{totallines:5} lines in list \n'
f'{emplines:5} empty lines in list \n'
f'{totalaveChars:5.1f} average characters per line \n'
f'{nonempaveChars:5.1f} average chars per non-empty line')
return result
print(stats(open('ASH.txt', 'r', encoding='utf8')))
Here is the result from stats:
13052 lines in list
2666 empty lines in list
44.6 average characters per line
56.0 average chars per non-empty line
Upvotes: 0
Views: 84
Reputation: 25023
elif parameter == 'statistics': outfile.write(stats(infile)) for line in infile: outfile.write(line)
... only the statistics go in and not the rest of the content in the original file ...
My educated guess is that the stats
function consumes and possibly
closes the input stream (IS).
If stats
is somehow well behaved and limits itself to consuming the
IS, one can rewind it
...
infile.seek(0) # rewind the input stream
for line in infile:
outfile.write(line)
If, on the other hand, stats
is a bit disruptive and closes
altogether the IS one can use the .name
attribute of the file object
to reopen it, like this
...
for line in open(infile.name):
outfile.write(line)
This second solution works even in the first, milder hypotesis and
works even if the code was passed the infile
file object from a
outer call.
Another possibility, if you can access and modify the stats
source
code, is to undo the reading performed by the function, memorizing
the current position in the input stream before any read
operation
and later rewind the IS to that position
def stats(infile):
...
current_pos = infile.tell()
# do your stuff
...
infile.seek(current_pos)
return workload
For this to work, of course, the file object has not to be closed
before the .seek()
, either explicitly (by a .close()
) or
implicitly (by falling outside the scope of a with
block).
If this is your situation (closed file), please remove either the
explicit infile.close()
or the (unnecessary) with
statement and
the rewind will be correct.
Upvotes: 0
Reputation: 5746
The issue is in the stats
function. The with statement will close the file with the local name text, which is infile in your case!
def stats(text) -> str:
totallines = 0
emplines = 0
characters = 0
for line in text:
totallines += 1
characters += len(line)
if len(line.strip()) == 0:
emplines += 1
lines = totallines - emplines
totalaveChars = characters/totallines
nonempaveChars = characters/lines
result = (f'{totallines:5} lines in list \n'
f'{emplines:5} empty lines in list \n'
f'{totalaveChars:5.1f} average characters per line \n'
f'{nonempaveChars:5.1f} average chars per non-empty line')
return result
In your main program, you passed to the function stats
the variable infile
, which is a file. You do not need to reopen it with with
inside the stats functions. Moreover, with
will ensure the closing at the end. Thus in your main loop, the infile
is closed after the call on stats
.
Upvotes: 4
Reputation: 3001
Try the following;
def copy_file():
infile_name = input("Please enter the name of the file to copy: ")
parameter = input("Please enter a parameter(line numbers, Gutenberg trim, statistics, none): ")
outfile_name = input("Please enter the name of the new copy: ")
counter = 1
with open(infile_name, 'r', encoding='utf8') as infile:
with open(outfile_name, 'w', encoding='utf8') as outfile:
if parameter == 'line numbers':
for line in infile:
outfile.write(f' {counter:6}: {line}')
counter += 1
elif parameter == 'Gutenberg trim':
copyStart = False
for line in infile:
#print(line.strip())
if '*** START' in line.strip():
copyStart = True
continue
elif '*** END' in line.strip():
copyStart = False
break
if copyStart == True:
outfile.write(line)
elif parameter == 'statistics':
outfile.write(stats(infile))
for line in infile:
outfile.write(line)
else:
for line in infile:
outfile.write(line)
copy_file()
Using with open(filename, 'r') as file:
it will automatically close the file once the operation has finished, and not before.
Upvotes: 0