312

What are all the common ways to read a file in Ruby?

For instance, here is one method:

fileObj = File.new($fileName, "r")
while (line = fileObj.gets)
  puts(line)
end
fileObj.close

I know Ruby is extremely flexible. What are the benefits/drawbacks of each approach?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
dsg
  • 12,924
  • 21
  • 67
  • 111
  • 7
    I don't think the current winning answer is correct. – inger Jan 20 '14 at 13:07
  • Does this answer your question? [How to read lines of a file in Ruby](https://stackoverflow.com/questions/6012930/how-to-read-lines-of-a-file-in-ruby) – Pedro Rolo Jan 25 '22 at 10:42

11 Answers11

375

The easiest way if the file isn't too long is:

puts File.read(file_name)

Indeed, IO.read or File.read automatically close the file, so there is no need to use File.open with a block.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
mckeed
  • 9,719
  • 2
  • 37
  • 41
  • 21
    `IO.read` or `File.read` also automatically close the file, though your wording makes it sound like they do not. – Phrogz Apr 05 '11 at 01:34
  • 15
    he already said "if the file isn't too long". Suits my case perfectly. – jayP Jun 25 '15 at 00:16
280
File.open("my/file/path", "r") do |f|
  f.each_line do |line|
    puts line
  end
end
# File is closed automatically at end of block

It is also possible to explicitly close file after as above (pass a block to open closes it for you):

f = File.open("my/file/path", "r")
f.each_line do |line|
  puts line
end
f.close
reducing activity
  • 1,985
  • 2
  • 36
  • 64
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • 15
    This is hardly idiomatic Ruby. Use `foreach` instead of `open` and dispense with the `each_line` block. – the Tin Man Jul 24 '14 at 18:59
  • 8
    `f.each { |line| ... }` and `f.each_line { |line| ... }` seem to have the same behavior (at least in Ruby 2.0.0). – chbrown Jan 12 '15 at 22:53
240

Be wary of "slurping" files. That's when you read the entire file into memory at once.

The problem is that it doesn't scale well. You could be developing code with a reasonably sized file, then put it into production and suddenly find you're trying to read files measuring in gigabytes, and your host is freezing up as it tries to read and allocate memory.

Line-by-line I/O is very fast, and almost always as effective as slurping. It's surprisingly fast actually.

I like to use:

IO.foreach("testfile") { |x| print "GOT ", x }

or

File.foreach('testfile') { |x| print "GOT", x }

File inherits from IO, and foreach is in IO, so you can use either.

I have some benchmarks showing the impact of trying to read big files via read vs. line-by-line I/O at "https://stackoverflow.com/q/25189262/128421".

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • 7
    This is exactly what I was looking for. I've got a file with five million lines, and really didn't want that loaded into memory. – Scotty C. May 12 '16 at 02:53
  • I wonder why is file "slurping" called like that? According [this dictionary](https://en.wiktionary.org/wiki/slurp) slurp means "To eat or drink noisily" . – builder-7000 Aug 14 '20 at 06:03
  • 1
    It comes from programming history. Here's a starting point https://www.perl.com/article/21/2013/4/21/Read-an-entire-file-into-a-string/ – the Tin Man Aug 28 '20 at 00:28
75

You can read the file all at once:

content = File.readlines 'file.txt'
content.each_with_index{|line, i| puts "#{i+1}: #{line}"}

When the file is large, or may be large, it is usually better to process it line-by-line:

File.foreach( 'file.txt' ) do |line|
  puts line
end

Sometimes you want access to the file handle though or control the reads yourself:

File.open( 'file.txt' ) do |f|
  loop do
    break if not line = f.gets
    puts "#{f.lineno}: #{line}"
  end
end

In case of binary files, you may specify a nil-separator and a block size, like so:

File.open('file.bin', 'rb') do |f|
  loop do
    break if not buf = f.gets(nil, 80)
    puts buf.unpack('H*')
  end
end

Finally you can do it without a block, for example when processing multiple files simultaneously. In that case the file must be explicitly closed (improved as per comment of @antinome):

begin
  f = File.open 'file.txt'
  while line = f.gets
    puts line
  end
ensure
  f.close
end

References: File API and the IO API.

Victor Klos
  • 398
  • 5
  • 11
  • 2
    There is no `for_each` in File or IO. Use [`foreach`](http://www.ruby-doc.org/core-2.1.2/IO.html#method-c-foreach) instead. – the Tin Man Jul 24 '14 at 18:57
  • 1
    I usually use the Sublime Text editor, with the RubyMarkers plugin, when documenting code to be used in answers here. It makes it really easy to show intermediate results, similar to using IRB. Also the Seeing Is Believing plugin for Sublime Text 2 is really powerful. – the Tin Man Aug 20 '14 at 22:18
  • 1
    Great answer. For the last example I might suggest using `while` instead of `loop` and using `ensure` to ensure the file gets closed even if an exception is raised. Like this (replace semi-colons with newlines): `begin; f = File.open('testfile'); while line = f.gets; puts line; end; ensure; f.close; end`. – antinome Sep 03 '15 at 16:15
  • 1
    yeah that is much better @antinome, improved the answer. thanks! – Victor Klos Oct 11 '15 at 20:36
31

One simple method is to use readlines:

my_array = IO.readlines('filename.txt')

Each line in the input file will be an entry in the array. The method handles opening and closing the file for you.

bta
  • 43,959
  • 6
  • 69
  • 99
  • 5
    As with `read` or any variant, this will pull the entire file into memory, which can cause major problems if the file is larger than the memory available. In addition, because it's an array, Ruby has to create the array, slowing the process additionally. – the Tin Man Sep 03 '13 at 21:17
25
file_content = File.read('filename with extension');
puts file_content;

http://www.ruby-doc.org/core-1.9.3/IO.html#method-c-read

Alex Weitz
  • 3,199
  • 4
  • 34
  • 57
Kumar KS
  • 873
  • 10
  • 21
9

I usually do this:

open(path_in_string, &:read)

This will give you the whole text as a string object. It works only under Ruby 1.9.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
sawa
  • 165,429
  • 45
  • 277
  • 381
4

if the file is small (slurping):

puts File.read("filename.txt")

if the file is big (streaming):

File.foreach("filename.txt") { |line| puts line }
sekmo
  • 1,517
  • 18
  • 27
3

return last n lines from your_file.log or .txt

path = File.join(Rails.root, 'your_folder','your_file.log')

last_100_lines = `tail -n 100 #{path}`
Alex Danko
  • 36
  • 1
  • 3
3

An even more efficient way is streaming by asking the operating system’s kernel to open a file, then read bytes from it bit by bit. When reading a file per line in Ruby, data is taken from the file 512 bytes at a time and split up in “lines” after that.

By buffering the file’s content, the number of I/O calls is reduced while dividing the file in logical chunks.

Example:

Add this class to your app as a service object:

class MyIO
  def initialize(filename)
    fd = IO.sysopen(filename)
    @io = IO.new(fd)
    @buffer = ""
  end

  def each(&block)
    @buffer << @io.sysread(512) until @buffer.include?($/)

    line, @buffer = @buffer.split($/, 2)

    block.call(line)
    each(&block)
  rescue EOFError
    @io.close
 end
end

Call it and pass the :each method a block:

filename = './somewhere/large-file-4gb.txt'
MyIO.new(filename).each{|x| puts x }

Read about it here in this detailed post:

Ruby Magic Slurping & Streaming Files By AppSignal

Khalil Gharbaoui
  • 6,557
  • 2
  • 19
  • 26
  • Watch out: that code will ignore the last line if it doesn't end with a linefeed (at least in Linux). – Jorgen Jan 26 '19 at 11:43
  • I think inserting "block.call(@buffer)" before "@io.close" will pick up the missing incomplete line. However, I have played with Ruby only one day so I could well be wrong. It worked in my application :) – Jorgen Jan 26 '19 at 12:27
  • After reading the AppSignal post it seems that there has been a small misunderstanding here. The code you copied out of that post which does a buffered IO is an example implementation of what Ruby actually does with File.foreach, or IO.foreach (which are the same method). They should be used, and you do not need reimplement them like this. – Peter H. Boling Apr 17 '20 at 09:04
  • @PeterH.Boling I'm also for the use-and-don't-reimplement mentality most of the time. But ruby does allow us to open up things and poke at their insides without shame it's one of it's perks. There is no real 'should' or 'should not' especially in ruby/rails. As long as you know what you are doing, and you write tests for it. – Khalil Gharbaoui Apr 17 '20 at 18:00
1
content = `cat file`

I think this method is the most "uncommon" one. Maybe it is kind of tricky, but it works if cat is installed.

helloqiu
  • 133
  • 6
  • 1
    A handy trick, but calling out to the shell has a lot of pitfalls, including 1) the commands may differ on different OS's, 2) you may need to escape spaces in the filename. You're much better off using Ruby built-in functions, e.g. `content = File.read(filename)` – Jeff Ward Nov 20 '18 at 17:30