Reading Files in Crystal

Reading and writing files are basic tasks needed for many Crystal programs. First we’ll look at some examples of reading files.

# Reading files requires checking most calls for errors.
# This helper will streamline our error checks below.
def check(e : Exception?)
  if e
    raise e
  end
end

# Perhaps the most basic file reading task is
# slurping a file's entire contents into memory.
dat = File.read("/tmp/dat")
print(dat)

# You'll often want more control over how and what
# parts of a file are read. For these tasks, start
# by opening a file to obtain a `File` object.
File.open("/tmp/dat") do |f|
  # Read some bytes from the beginning of the file.
  # Allow up to 5 to be read but also note how many
  # actually were read.
  b1 = Bytes.new(5)
  n1 = f.read(b1)
  puts "#{n1} bytes: #{String.new(b1[0, n1])}"

  # You can also seek to a known location in the file
  # and read from there.
  f.seek(6, IO::Seek::Set)
  b2 = Bytes.new(2)
  n2 = f.read(b2)
  print "#{n2} bytes @ #{f.pos - n2}: "
  puts String.new(b2[0, n2])

  # Other methods of seeking are relative to the
  # current cursor position,
  f.seek(4, IO::Seek::Current)

  # and relative to the end of the file.
  f.seek(-10, IO::Seek::End)

  # The `IO` module provides some methods that may
  # be helpful for file reading. For example, reads
  # like the ones above can be more robustly
  # implemented with `IO#read_fully`.
  f.seek(6, IO::Seek::Set)
  b3 = Bytes.new(2)
  n3 = f.read_fully(b3)
  puts "#{n3} bytes @ #{f.pos - n3}: #{String.new(b3)}"

  # There is no built-in rewind, but
  # `seek(0, IO::Seek::Set)` accomplishes this.
  f.seek(0, IO::Seek::Set)

  # The `IO::Buffered` module implements a buffered
  # reader that may be useful both for its efficiency
  # with many small reads and because of the additional
  # reading methods it provides.
  r4 = IO::Buffered.new(f)
  b4 = r4.peek(5)
  puts "5 bytes: #{String.new(b4)}"

  # The file is automatically closed when the block ends.
end

To run this program:

$ echo "hello" > /tmp/dat
$ echo "crystal" >>   /tmp/dat
$ crystal reading-files.cr
hello
crystal
5 bytes: hello
2 bytes @ 6: cr
2 bytes @ 6: cr
5 bytes: hello

This Crystal code demonstrates various methods of reading files, including reading the entire file at once, reading specific bytes, seeking to different positions in the file, and using buffered readers. The File.open method with a block is used to ensure the file is properly closed after use.

Next, we’ll look at writing files.