I/O Operations in Ruby
Learn console input and output, formatted printing, file reading and writing, and I/O error handling in Ruby with Docker-ready examples
Input and output are where a program meets the outside world—reading what a user types, writing results to the screen, and persisting data to files. Ruby treats all of these through the same lens as everything else in the language: I/O is done with objects. The console is represented by the IO objects $stdin and $stdout, files are File objects (a subclass of IO), and familiar methods like puts and gets are just convenient shortcuts that delegate to them.
Because Ruby blends object-oriented and functional styles, its most idiomatic I/O pattern is passing a block to a method like File.open or File.foreach. The method opens the resource, yields it to your block, and guarantees it is closed afterward—even if an exception is raised. Where other languages rely on manual close() calls or special syntax like try-with-resources, Ruby gets automatic cleanup from an ordinary block.
The Hello World tutorial already introduced basic output with puts, print, and p. This tutorial goes further: formatted output with printf and format, reading user input from standard input with gets, writing and reading files several idiomatic ways, and handling I/O errors with begin/rescue/ensure.
Formatted Output
Beyond plain puts, Ruby offers C-style formatting through printf, the format method (also spelled sprintf), and the % operator on strings. All three use the same format specifiers: %s for strings, %d for integers, %f for floats, with optional width, precision, and alignment flags.
Create a file named formatted_output.rb:
| |
The difference between the three is where the result goes: printf prints immediately, while format and % return a string you can store, pass around, or print later. The width flags (%-10s, %8.2f, %04d) are handy for lining up columns in reports and tables.
Reading Console Input
The gets method reads one line from standard input, including the trailing newline. Chaining .chomp strips that newline—so gets.chomp is one of the most common idioms in Ruby. Input always arrives as a string; convert it with .to_i or .to_f when you need a number.
Create a file named console_input.rb:
| |
Note that .to_i never raises an error: "30".to_i is 30, but "abc".to_i is simply 0. That makes quick scripts forgiving, though you should validate input when correctness matters (Ruby also offers Integer("abc"), which raises an ArgumentError instead of silently returning 0).
Writing Files
Ruby gives you a spectrum of file-writing tools, from the one-line File.write for simple cases to File.open with a block for full control. The block form is the idiomatic choice when writing multiple times: the file closes automatically when the block ends.
Create a file named file_write.rb:
| |
The mode string works like C’s fopen: "w" truncates and writes, "a" appends, "r" reads (the default). Inside the block, the File object responds to the same output methods you already know—puts, print, write—because File inherits them from IO.
Reading Files
Reading mirrors writing: File.read slurps the whole file into one string, File.readlines returns an array of lines, and File.foreach streams the file one line at a time without loading it all into memory—the right choice for large files.
Create a file named file_read.rb:
| |
This example expects gemstones.txt to exist, so run file_write.rb first. Notice the functional flavor of the last example: File.foreach returns an enumerator, so it chains with with_index just like any other Ruby collection—no explicit loop counter needed.
Handling I/O Errors
File operations fail in the real world: files go missing, permissions are wrong, disks fill up. Ruby raises exceptions for these—a missing file raises Errno::ENOENT. Handle them with begin/rescue, and use ensure for cleanup code that must run no matter what. You can also avoid some exceptions entirely by asking first with File.exist?.
Create a file named io_errors.rb:
| |
The Errno family maps operating system error codes to exception classes (Errno::ENOENT for a missing file, Errno::EACCES for a permission error), and all of them inherit from SystemCallError, so you can rescue that class to catch any OS-level I/O failure at once.
Running with Docker
| |
Because the current directory is mounted at /app, gemstones.txt is written to your host machine and persists between the separate docker run commands.
Expected Output
formatted_output.rb
Standard puts output
Name: Ada, Score: 92.5
Ada | 92.50|
Line 0007
console_input.rb
With Matz and 30 piped in as answers:
What is your name?
How many years have you used Ruby?
Hello, Matz!
That's about 360 months of Ruby.
file_write.rb
Wrote 29 bytes to gemstones.txt
file_read.rb
The file has 29 characters
First gemstone: ruby
Total gemstones: 5
1: ruby
2: pearl
3: opal
4: garnet
5: topaz
io_errors.rb
Error: missing.txt does not exist
Cleanup always runs
gemstones.txt is 29 bytes
Key Concepts
- I/O is object-oriented: the console and files are all
IOobjects, soputs,print, andwritework the same on$stdoutand on an openFile getsincludes the newline—gets.chompis the standard idiom for reading a clean line of input, and.to_i/.to_fconvert it to a number- Prefer the block form of
File.open: the file is closed automatically when the block ends, even if an exception is raised mid-write - Pick the right reading tool:
File.readfor a whole file as one string,File.readlinesfor an array of lines,File.foreachto stream large files line by line File.foreachreturns an enumerator, so it composes with methods likewith_index—Ruby’s functional side applied to I/O- Formatted output comes three ways:
printfprints directly, whileformatand the%operator return a string using the same%s/%d/%fspecifiers - I/O failures raise exceptions such as
Errno::ENOENT; rescue them withbegin/rescue, put mandatory cleanup inensure, or pre-check withFile.exist? .to_isilently returns 0 for non-numeric input; useInteger(string)when you want invalid input to raise an error instead
Comments
Loading comments...
Leave a Comment