# Files, JSON, CSV and Time — Ruby

Source: https://www.geekswithgeeks.com/en/ruby/p-files

> 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.](assets/figures/ruby/section-6-map.svg) — Figure 6.1 — A file-processing script.

## Summarising a CSV and writing JSON

Streaming rows, grouping and writing a report.

```ruby
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.

**Quiz:** Why use the block form File.open(path, "w") { |f| ... }?

- [ ] It is faster
- [ ] It encrypts the file
- [x] 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.
