rubyguides

Kernel#readlines

ios.readlines(separator=$/) -> [string]

The readlines method reads the entire contents of an IO object (including files) and returns all lines as an array. Each line is a separate string element in the returned array, with the line separator removed by default.

That eager behavior is useful when you need every line in memory at once, such as when you are building a quick report, counting lines, or doing a pass that needs random access to the whole file. It is less suitable for very large inputs, but for moderate files it keeps the code simple and easy to follow.

Because the result is already an array, later steps can sort, filter, or index the lines without reopening the file. That convenience is often the main reason to choose readlines over a streaming approach.

How it works

When you call readlines on a file or IO object, Ruby reads through the entire file from the current position to the end, splitting the content into lines and returning them as an array. This is equivalent to calling each_line.to_a but more concise.

Practical Examples

Reading entire file at once

# Read all lines from a file
lines = File.readlines("data.txt")
puts lines[0]  # First line
puts lines.length  # Number of lines

# Process each line
lines.each { |line| puts line.upcase }

This is the straightforward whole-file style. It is easy to read because the code first collects the lines and then processes the array one step at a time. For moderate file sizes, the clarity of having a complete array up front outweighs the memory cost of loading the entire file in one call.

With custom separator

# Read lines separated by commas (CSV-like)
csv_content = "apple,banana,cherry\norange,grape,mango"
lines = csv_content.readlines(",")
# => ["apple", "banana", "cherry\n", "orange", "grape", "mango"]

# Read paragraphs (separated by double newlines)
paragraphs = File.readlines("document.txt", "")

Custom separators are useful when the file is organized into chunks instead of ordinary lines. That lets readlines adapt to formats like paragraphs or delimited text without a separate parser, so you get clean, pre-split chunks straight from the read call rather than having to split them in a second pass.

Reading from standard input

# Read all lines from stdin until EOF
puts "Enter lines (Ctrl+D to end):"
lines = STDIN.readlines
puts "You entered #{lines.length} lines"

Reading from standard input is the same pattern, just pointed at interactive data instead of a file on disk. It is handy in small scripts that wait for pasted text or piped input, and the array return value means you can treat typed or piped data the same way you would treat a file on disk.

Processing log files

# Analyze log file
log_lines = File.readlines("application.log")

# Find all error entries
errors = log_lines.select { |line| line.include?("ERROR") }
puts "Found #{errors.length} errors"

# Find lines after a timestamp
critical = log_lines.select { |line| line.match?(/CRITICAL/) }

The array form makes it easy to apply several filters after the lines are loaded. That works well when the file is small enough to fit comfortably in memory and the follow-up analysis needs random access without reopening the source. For log files under a few megabytes, this approach is both simple and fast.

Practical data processing

# Process CSV-like data
data = File.readlines("data.txt")
parsed = data.map do |line|
  line.chomp.split(",")
end

# Filter and transform
results = File.readlines("input.txt")
  .reject { |line| line.start_with?("#") }  # Skip comments
  .map(&:strip)
  .reject(&:empty?)

That pipeline shows the main trade-off of readlines: you get a complete array up front, which makes chained array operations convenient. The cost is memory, so it fits moderate files better than huge ones.

Memory considerations

readlines loads the entire file into memory, which can be problematic for very large files. For large files, consider using each_line instead:

# Memory-efficient alternative for large files
File.open("huge_file.txt", "r") do |file|
  file.each_line do |line|
    process(line)
  end
end

That streaming pattern is the better choice when file size matters more than random access. You trade the convenience of an array for lower memory usage and a smaller working set, but you also lose the ability to jump to an arbitrary line by index without re-reading from the start.

Performance tips

# Specify separator for better performance when you know the format
lines = File.readlines("file.txt", chomp: true)  # Ruby 2.5+

# Use encoding when dealing with non-UTF8 files
lines = File.readlines("file.txt", encoding: "ISO-8859-1")

Those options are worth setting explicitly when your files are large or come from different environments. The method itself is simple, but a few upfront choices — like chomp: true to strip newlines and an explicit encoding to avoid character conversion surprises — can keep the rest of the pipeline predictable and portable.

The readlines method is a convenient way to work with file contents when you need all lines in memory at once, making it ideal for files that fit comfortably in memory.

If you only need to stream through the file once, each_line is often a better match. The method choice is really about whether the whole file needs to become an array or whether you can process each line as it arrives.

See Also