Ruby's File.open and the need for f.close
I saw many times in ruby codes unmatched
File.open
calls
Can you give an example? I only ever see that in code written by newbies who lack the "common knowledge in most programming languages that the flow for working with files is open-use-close".
Experienced Rubyists either explicitly close their files, or, more idiomatically, use the block form of File.open
, which automatically closes the file for you. Its implementation basically looks something like like this:
def File.open(*args, &block)
return open_with_block(*args, &block) if block_given?
open_without_block(*args)
end
def File.open_without_block(*args)
# do whatever ...
end
def File.open_with_block(*args)
yield f = open_without_block(*args)
ensure
f.close
end
Scripts are a special case. Scripts generally run so short, and use so few file descriptors that it simply doesn't make sense to close them, since the operating system will close them anyway when the script exits.
Do we need to explicitly close?
Yes.
If yes then why does the GC autoclose?
Because after it has collected the object, there is no way for you to close the file anymore, and thus you would leak file descriptors.
Note that it's not the garbage collector that closes the files. The garbage collector simply executes any finalizers for an object before it collects it. It just so happens that the File
class defines a finalizer which closes the file.
If not then why the option?
Because wasted memory is cheap, but wasted file descriptors aren't. Therefore, it doesn't make sense to tie the lifetime of a file descriptor to the lifetime of some chunk of memory.
You simply cannot predict when the garbage collector will run. You cannot even predict if it will run at all: if you never run out of memory, the garbage collector will never run, therefore the finalizer will never run, therefore the file will never be closed.
Is it bad practice to not close the file in this snippet?
Not closing files is always bad practice unless you are using something like the with
statement in python.
While a scripting language will usually close open files on exit, it's cleaner to do it as soon as you are done with the file - especially when writing to it.
Apparently Ruby has something similar to python's with
:
File.open(from_file, 'r') do |f_in|
File.open(to_file, 'w') do |f_out|
f_out.write(f_in.read)
end
end
Relevant docs: http://ruby-doc.org/core-1.9.3/File.html#method-c-open
Undefined method 'close' when trying to close a file in Ruby
Your input
is not a file object because of the read()
method call:
input = File.open(from_file).read()
Since read
returns either nil
or ""
depending upon the length parameter to read, calling input.close()
will raise undefined method close
as input
in your case is a string and String
does not have close()
method.
So instead of calling File.open(from_file).read()
and calling the close()
method, you can just call the File.read()
:
from_file, to_file = ARGV
script = $0
puts "Copying from #{from_file} to #{to_file}."
input = File.read(from_file)
puts "The input file is #{input.length} bytes long."
puts "Does the output file exist? #{File.exists? to_file}"
puts "Ready, hit RETURN to contine, CTRL-C to abort."
STDIN.gets
output = File.open(to_file, 'w')
output.write(input)
puts "Alright, all done."
output.close()
File.open with block vs without
DarkDust already said that these methods are different. I'll explain you the blocks a little more, as I suppose that I can guess why you asked this question ;-)
The block in ruby is just a parameter for some method. It's not just a different syntax.
Methods which accept (optional) blocks usually have a condition to test whether they have been called with block, or without.
Consider this very simplified example: (the real File.open is similar, but it ensures the file is closed even if your block raises an error, for example)
def open(fname)
self.do_open(fname)
if block_given?
yield(self) # This will 'run' the block with given parameter
self.close
else
return self # This will just return some value
end
end
In general, every method may work (works) differently with a block or without a block. It should be always stated in the method documentation.
Testing and closing file objects while using let in ruby rspec
If you're going to use a_file
in just one test, then your second example is good.
it "is a File" do
Foo.open('blah.txt') do |f|
expect(f).to be_a File
end
end
If you'll use a_file
multiple times, you could do this:
before do
@file = Foo.open('blah.txt')
end
after do
@file.close
end
it "is a File" do
expect(@file).to be_a File
end
...
Does a ruby loop reading a file close the file afterwards?
What i want to know is is this file closed at the end of this piece of code or should i still be calling:
Yes it is closed.IO::open says
With no associated block, IO.open is a synonym for ::new. If the optional code block is given, it will be passed io as an argument, and the IO object will automatically be closed when the block terminates. In this instance, ::open returns the value of the block.
f = File.open('doc.txt') do |file|
file
end
f.closed? # => true
or should i still be calling:
File.close() ?
Yes you can,if your block return the file
object,before terminating,like my above code.Or if you assign the file
object inside a block to a local variable as below :
f = nil
File.open('doc.txt') do |file|
f = file
# your code
end
f.closed? # => true
After reading a file ruby leaves it open/locked on Windows XP
The documentation is clear. However, you're passing the block to collect
. And since you're not passing it to open
, you are responsible for closing the file.
To have file auto-closed, try this:
File.open(file,"r") do |f|
f.collect # or whatever
end
Write to a file in Ruby with every iteration of a loop
Let's use IO::write to create two input files.
FNameIn1 = 'in1'
File.write(FNameIn1, "cow\npig\ngoat\nhen\n")
#=> 17
We can use IO::read to confirm what was written.
puts File.read(FNameIn1)
cow
pig
goat
hen
FNameIn2 = 'in2'
File.write(FNameIn2, "12\n34\n56\n78\n")
#=> 12
puts File.read(FNameIn2)
12
34
56
78
Next, use File::open to open the two input files for reading, obtaining a file handle for each.
f1 = File.open(FNameIn1)
#=> #<File:in1>
f2 = File.open(FNameIn2)
#=> #<File:in2>
Now open a file for writing.
FNameOut = 'out'
f = File.open(FNameOut, "w")
#=> #<File:out>
Assuming the two input files have the same number of lines, in a while
loop read the next line from each, combine the two lines in some ways and the write the resulting line to the output file.
until f1.eof
line11 = f1.gets.chomp
line12 = f1.gets.chomp
line21 = f2.gets.chomp
line22 = f2.gets.chomp
f.puts "%s %s, %s %s" % [line11, line21, line12, line22]
end
See IO#eof, IO#gets and IO#puts.
Lastly, use IO#close to close the files.
f1.close
f2.close
f.close
Let's see that FileOut
looks like.
puts File.read(FNameOut)
cow 12, pig 34
goat 56, hen 78
We can have Ruby close the files by using a block for each File::open
:
File.open(FNameIn1) do |f1|
File.open(FNameIn2) do |f2|
File.open(FNameOut, "w") do |f|
until f1.eof
line11 = f1.gets.chomp
line12 = f1.gets.chomp
line21 = f2.gets.chomp
line22 = f2.gets.chomp
f.puts "%s %s, %s %s" % [line11, line21, line12, line22]
end
end
end
end
puts File.read FNameOut
cow 12, pig 34
goat 56, hen 78
This is in fact how it's normally done in Ruby, in part to avoid the possibility of forgetting to close files.
Here's another way, using IO::foreach, which, without a block, returns an enumerator, allowing the use of Enumerable#each_slice, as referenced in the question.
e1 = File.foreach(FNameIn1).each_slice(2)
#=> #<Enumerator: #<Enumerator: File:foreach("in1")>:each_slice(2)>
e2 = File.foreach(FNameIn2).each_slice(2)
#=> #<Enumerator: #<Enumerator: File:foreach("in2")>:each_slice(2)>
File.open(FNameOut, "w") do |f|
loop do
line11, line12 = e1.next.map(&:chomp)
line21, line22 = e2.next.map(&:chomp)
f.puts "%s %s, %s %s" % [line11, line21, line12, line22]
end
end
puts File.read(FNameOut)
cow 12, pig 34
goat 56, hen 78
We may observe the values generated by the enumerator
e1 = File.foreach(FNameIn1).each_slice(2)
by repeatedly executing Enumerator#next:
e1.next
#=> ["cow\n", "pig\n"]
e1.next
#=> ["goat\n", "hen\n"]
e1.next
#=> StopIteration (iteration reached an end)
The StopIteration
exception, when raised, is handled by Kernel#loop by breaking out of the loop (which is one reason why loop
is so useful).
How would you close this file descriptor?
If you are just copying the file...
you could just use FileUtils#cp:
FileUtils.cp("from_file", "to_file")
or even shell-out to the operating system and do it with a system command.
Let's suppose you want to do something to the input file before writing it to the output file.
If from_file
is not large,...
you could "gulp it" into a string using IO.read:
str = IO.read(from_file)
manipulate str
as desired, to obtain new_str
, then then blast it to the output file using IO#write:
IO.write("to_file", new_str)
Note that for the class File:
File < IO #=> true # File inherits IO's methods
which is why you often see this written File.read(...)
and File.write(...)
.
If from_file
is large, read a line, write a line...
provided the changes to be made are done for each line separately.
f = File.open("to_file", "w") # or File.new("to_file", "w")
IO.foreach("from_file") do |line|
# < modify line to produce new_line >
f.puts new_line
end
f.close
foreach
closes "from_file"
when it's finished. If f.close
is not present, Ruby will close "to_file"
when the method containing the code goes out of scope. Still, it's a good idea to close it in case other work is done before the code goes out of scope.
Related Topics
How to Update Ruby Gems from Behind a Proxy (Isa-Ntlm)
Difference Between Class Variables and Class Instance Variables
Ruby: String Comparison Issues
Difference Between \A \Z and ^ $ in Ruby Regular Expressions
Rvm Installation Not Working: "Rvm Is Not a Function"
Sudo Gem Install' or 'Gem Install' and Gem Locations
How to Track System-Specific Config Files in a Repo/Project
What's the Difference Between Ruby'S Dup and Clone Methods
Look Up All Descendants of a Class in Ruby
Connecting Rails 3.1 With Multiple Databases
How to "Pretty" Format Json Output in Ruby on Rails
How to Match All Occurrences of a Regex
Get the Name of the Currently Executing Method