Labs ICT
โญ Pro Login

File I/O

Reading and writing files.

File I/O

Working with files is a fundamental part of programming. Ruby makes file I/O simple and clean. You can read files, write files, iterate line by line, and work with directories โ€” all with elegant, readable code.

# Reading an entire file
content = File.read("hello.txt")
puts content

# Writing to a file
File.write("output.txt", "Hello, World!\n")

# Appending to a file
File.open("log.txt", "a") do |file|
  file.puts "New log entry at #{Time.now}"
end
Try it Yourself ->

Block Form: Automatic Closing

When you pass a block to File.open, Ruby automatically closes the file when the block ends. This is safer than manually opening and closing files because it handles errors gracefully.

# Block form โ€” file is automatically closed
File.open("data.txt", "w") do |file|
  file.puts "Line 1"
  file.puts "Line 2"
  file.puts "Line 3"
end
# File is closed here, even if an error occurs

# Reading with block
File.open("data.txt", "r") do |file|
  file.each_line do |line|
    puts "Read: #{line.chomp}"
  end
end
Try it Yourself ->

Line-by-Line Processing

For large files, you don't want to load everything into memory. Use File.foreach or file.each_line to process one line at a time. This is memory efficient and handles files of any size.

# File.foreach โ€” doesn't need File.open
File.foreach("data.txt") do |line|
  puts line.chomp
end

# each_line on a file object
File.open("large.log", "r") do |file|
  file.each_line.with_index do |line, index|
    puts "#{index + 1}: #{line.chomp}" if line.include?("ERROR")
  end
end

# Collect lines into an array
lines = File.readlines("data.txt")
puts lines.length  # => number of lines
Try it Yourself ->

Dir.glob and Pathname

Dir.glob finds files matching patterns. Pathname gives you object-oriented path manipulation. Together they make file system operations clean and portable.

# Find all Ruby files
ruby_files = Dir.glob("*.rb")
puts ruby_files.inspect

# Recursive search
all_ejs = Dir.glob("content/**/*.ejs")
puts all_ejs.length

# Pathname for path manipulation
require "pathname"
path = Pathname.new("/home/user/documents/file.txt")
puts path.dirname    # => "/home/user/documents"
puts path.basename   # => "file.txt"
puts path.extname    # => ".txt"
puts path.exist?     # => true or false

# Join paths portably
base = Pathname.new("/app")
config = base + "config" + "database.yml"
puts config.to_s     # => "/app/config/database.yml"
Try it Yourself ->

Error Handling with Files

File operations can fail โ€” the file might not exist, you might not have permission, or the disk might be full. Always handle errors when working with files.

# Check if file exists before reading
if File.exist?("config.yml")
  config = File.read("config.yml")
  puts config
else
  puts "Config file not found!"
end

# Rescue block for error handling
begin
  content = File.read("important.txt")
  puts content
rescue Errno::ENOENT
  puts "File not found"
rescue Errno::EACCES
  puts "Permission denied"
rescue => e
  puts "Error: #{e.message}"
ensure
  puts "Cleanup code runs regardless"
end

# Safe reading with rescue
content = File.read("maybe_exists.txt") rescue "default content"
puts content
Try it Yourself ->

๐Ÿงช Quick Quiz

How do you read a file line by line?