पाठ 16 / 25

Files, JSON, CSV and Time

Read and write files and common data formats, and handle time correctly.

Everyday scripting tasks

Ruby is excellent for scripts that process files and data. File.read and File.write handle small files; File.foreach(path) streams line by line for large ones; and the block form File.open(path, "w") { |f| ... } closes the file automatically, even on errors. Dir.glob("data/*.csv") lists files and FileUtils copies and moves them. Pathname offers an object-oriented path API. The json library parses with JSON.parse(text, symbolize_names: true) and generates with to_json or JSON.pretty_generate. The csv library (a bundled gem since Ruby 3.4, so add it to your Gemfile) reads CSV with headers (CSV.foreach(path, headers: true)) and writes it safely with quoting. For dates and times, use Time (with time zones via Time.now.utc, getlocal("+05:30") or the tzinfo gem), Date from the date library, and ISO 8601 strings for interchange (Time.now.utc.iso8601). Use Process.clock_gettime(Process::CLOCK_MONOTONIC) for measuring durations, not wall-clock time.

Scripting with files

Ruby scripts read files, transform data and write results in a few readable lines.

A folder of document icons on the left, a gear in the middle and a neat report document on the right.
Figure 6.1 — A file-processing script.

Summarising a CSV and writing JSON

Streaming rows, grouping and writing a report.

require "csv"
require "json"
require "time"

totals = Hash.new(0)
counts = Hash.new(0)

CSV.foreach("data/orders.csv", headers: true) do |row|     # streams one row at a time
  city = row["city"].strip.capitalize
  totals[city] += Integer(row["total_paise"])
  counts[city] += 1
end

report = totals.map do |city, paise|
  { city:, orders: counts[city], revenue_rupees: (paise / 100.0).round(2) }
end.sort_by { -_1[:revenue_rupees] }

File.write("output/revenue.json", JSON.pretty_generate({
  generated_at: Time.now.utc.iso8601,
  cities: report
}))

Dir.glob("data/*.csv").each { |path| puts "#{File.basename(path)}: #{File.size(path)} bytes" }

Integer() fails loudly, to_i does not

"12abc".to_i silently returns 12, and "abc".to_i returns 0. Integer("abc") raises an error. Use Integer() and Float() when parsing data that must be valid.

त्वरित जाँच: Why use the block form File.open(path, "w") { |f| ... }?

  • It is faster
  • It encrypts the file
  • The file is closed automatically when the block ends, even if an error occurs
  • It is the only way to write files
Answer

The file is closed automatically when the block ends, even if an error occurs — Block forms guarantee resource cleanup.