Ruby'S File.Open and the Need For F.Close

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



Leave a reply



Submit