Error in Lua: attempt to use a closed file. when using io.close() after reading files

284 Views Asked by At

I am learning Lua with youtube video and it introduces a way to write a text file like this:

io.output("myfile.txt")
print("My name is David") -- This won't write into the file, just print to the console
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.close()

Then it introduces a way to read text file like this:

io.input("myfile.txt")
print(io.read("*all"))
io.close()

These two code blocks works when they are in the separate lua files. However, when I combine these two code blocks into a single lua file. It generates an error.

The combined file looks like this:

-- Write file and close it
io.output("myfile.txt")
print("My name is David") -- This won't write into the file, just print to the console
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.close()

-- Read file and close it
io.input("myfile.txt")
print(io.read("*all"))
io.close()

and it shows an error when i run this file:

lua54: file.lua:36: attempt to use a closed file
stack traceback:
        [C]: in function 'io.close'
        file.lua:36: in main chunk
        [C]: in ?

The output that I expect is:

My name is David
hello world, how are you
hello world, how are you
hello world, how are you
hello world, how are you

In the meantime I also tried to delete the first io.close() so the code looks like this:

-- Write file and close it
io.output("myfile.txt")
print("My name is David") -- This won't write into the file, just print to the console
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")

-- Read file and close it
io.input("myfile.txt")
print(io.read("*all"))
io.close()

But the console output only shows the print() statement:

My name is David

I also tried to keep the first io.close() and delete second io.close(). I got the expected output.

The code looks like this:

-- Write file and close it
io.output("myfile.txt")
print("My name is David") -- This won't write into the file, just print to the console
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.write("hello world, how are you\n")
io.close()

-- Read file and close it
io.input("myfile.txt")
print(io.read("*all"))

Output:

My name is David
hello world, how are you
hello world, how are you
hello world, how are you
hello world, how are you

Could anyone explain why there is an error and what do these codes do, Thanks!

2

There are 2 best solutions below

4
On BEST ANSWER

io.close() closes the default output file.
To close the default input file, use io.close(io.input())

0
On

More important than io.close() is io.flush() in your case.
Example (typed in Lua 5.1 Standalone)

$ readline-editor lua5.1
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> io.output("myfile.txt")
> io.write("Line 1\n")
> io.write("Line 2\n")
> io.write("Line 3\n")
> io.write("Line 4")
> io.flush() -- This writes the Buffered Text into the File (finally)
> -- io.flush() even has to be used if Text
> -- (or Chars) outputted slowly within a loop
> -- and a sleep() or wait() is used
> -- Example:
> -- do local txt = "Hey"
> -- for i = 1, #txt do
> --   io.write(txt:sub(i, i))
> --   io.flush() -- Necessary before a sleep() or wait()
> --   sleep(0.25)
> -- end
> -- io.write('\n')
> -- end
> print("My Name Is Lx")
My Name Is Lx
> return(io.open("myfile.txt"):read("*a"))
Line 1
Line 2
Line 3
Line 4
> print(io.open("myfile.txt"):read("*a"))
Line 1
Line 2
Line 3
Line 4
> io.output(io.stdout)
> io.write(io.open("myfile.txt"):read("*a"))
Line 1
Line 2
Line 3
Line 4> 

For me the best Method to close probably unkown and open File Handle' are...

collectgarbage("collect")

Because the Datatype userdata has a MetaMethod __gc that closes such free unknown Zombies
Example

> io.open("myfile.txt", "w"):write(("Used by Lua: %d\n"):format(collectgarbage("count")))
> io.write(io.open("myfile.txt"):read("*a")) -- Nothing
> io.write(io.open("myfile.txt"):read("*a")) -- Nothing
> collectgarbage("collect") -- Flush & Close open File Handle
> io.write(io.open("myfile.txt"):read("*a"))
Used by Lua: 18

file:close ()
Closes file. Note that files are automatically closed when their handles are garbage collected, but that takes an unpredictable amount of time to happen.
Reference: https://www.lua.org/manual/5.1/manual.html#5.7

PS: The Garbage Collector can be tuned and this can also be interest for embedded WiFi IoT Device Developer which always have to little Memory for Lua
So check out this...

> collectgarbage("setpause", 8)
> collectgarbage("restart")
> io.open("myfile.txt", "w"):write(("%s Bytes used by Lua: %.f\n"):format(os.date("[%T]"), collectgarbage("count") * 1024))
> io.write(io.open("myfile.txt"):read("*a"))
[23:51:55] Bytes used by Lua: 18244
> io.open("myfile.txt", "w"):write(("%s Bytes used by Lua: %.f\n"):format(os.date("[%T]"), collectgarbage("count") * 1024))
> io.write(io.open("myfile.txt"):read("*a"))
[23:52:11] Bytes used by Lua: 18244
> return(("%s Bytes used by Lua: %.f\n"):format(os.date("[%T]"), collectgarbage("count") * 1024))
[23:52:32] Bytes used by Lua: 18065

> return(("%s Bytes used by Lua: %.f\n"):format(os.date("[%T]"), collectgarbage("count") * 1024))
[23:52:40] Bytes used by Lua: 18041

> return(("%s Bytes used by Lua: %.f\n"):format(os.date("[%T]"), collectgarbage("count") * 1024))
[23:52:42] Bytes used by Lua: 18041

> return(("%s Bytes used by Lua: %.f\n"):format(os.date("[%T]"), collectgarbage("count") * 1024))
[23:52:44] Bytes used by Lua: 18041