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 ->