Writing to a File

Problem

You want to write some text or Ruby data structures to a file. The file might or might not exist. If it does exist, you might want to overwrite the old contents, or just append new data to the end of the file.

Solution

Open the file in write mode ('w'). The file will be created if it doesn't exist, and truncated to zero bytes if it does exist. You can then use IO#write or the << operator to write strings to the file, as though the file itself were a string and you were appending to it.

You can also use IO#puts or IO#p to write lines to the file, the same way you can use Kernel#puts or Kernel#p to write lines to standard output.

Both of the following chunks of code destroy the previous contents of the file output, then write a new string to the file:

open('output', 'w') { |f| f << "This file contains great truths. " } open('output', 'w') do |f| f.puts 'The great truths have been overwritten with an advertisement.' end open('output') { |f| f.read } # => "The great truths have been overwritten with an advertisement. "

To append to a file without overwriting its old contents, open the file in append mode ('a') instead of write mode:

open('output', "a") { |f| f.puts 'Buy Ruby(TM) brand soy sauce!' } open('output') { |f| puts f.read } # The great truths have been overwritten with an advertisement. # Buy Ruby(TM) brand soy sauce!

 

Discussion

Sometimes you'll only need to write a single (possibly very large) string to a file. Usually, though, you'll be getting your strings one at a time from a data structure or some other source, and you'll call puts or the append operator within some kind of loop:

open('output', 'w') do |f| [1,2,3].each { |i| f << i << ' and a ' } end open('output') { |f| f.read } # => "1 and a 2 and a 3 and a "

Since the << operator returns the filehandle it wrote to, you can chain calls to it. As seen above, this feature lets you write multiple strings to a file in a single line of Ruby code.

Because opening a file in write mode destroys the file's existing contents, you should only use it when you don't care about the old contents, or after you've read them into memory for later use. Append mode is nondestructive, making it useful for files like log iles, which need to be updated periodically without destroying their old contents.

Buffered I/O

There's no guarantee that data will be written to your file as soon as you call << or puts. Since disk writes are expensive, Ruby lets changes to a file pile up in a buffer. It occasionally flushes the buffer, sending the data to the operating system so it can be ritten to disk.

You can manually flush Ruby's buffer for a particular file by calling its IO#flush method. You can turn off Ruby's buffering altogether by setting IO.sync to false. However, your operating system probably does some disk buffering of its own, so doing these things won't neccessarily write your changes directly to disk.

open('output', 'w') do |f| f << 'This is going into the Ruby buffer.' f.flush # Now it's going into the OS buffer. end IO.sync = false open('output', 'w') { |f| f << 'This is going straight into the OS buffer.' }

 

See Also

Категории