Ruby’s CSV library can parse a string or an IO source and can generate CSV output. The reliable approach is to identify the file’s actual separators, line endings, quoting, headers, and encoding, then set options deliberately instead of assuming every file uses the defaults. The examples here target the Ruby 3.3 CSV class reference and CSV gem 3.3.2 documentation.
How do I parse a CSV file in Ruby?
For a small string, use CSV.parse. For a file or other stream, pass an IO to CSV.foreach, CSV.read, or construct a CSV object. With default settings, Ruby expects comma-separated columns, double-quote quoting, automatically detected row separators, and no header row.
require "csv"
text = "name,agenAda,36nGrace,28n"
rows = CSV.parse(text)
# => [["name", "age"], ["Ada", "36"], ["Grace", "28"]]
CSV.foreach("people.csv") do |row|
p row
end
CSV.parse returns all records, which is convenient when the input fits comfortably in memory. CSV.foreach yields one record at a time and is the better shape for a large file.
String input versus an IO source
| Input pattern | Typical API | Result | Use it when |
|---|---|---|---|
| String | CSV.parse(text, ...) |
All parsed records | The data is already in memory or is small enough to load at once |
| File or other IO | CSV.foreach(path, ...) |
Records yielded incrementally | You want bounded memory use while processing a file |
| String or IO wrapped in an object | CSV.new(source, ...) |
A configurable CSV parser/generator object | You need to call methods on a reusable CSV object |
CSV.new wraps a String in a StringIO positioned at the beginning. An IO should be open for reading and positioned at the beginning. Options supplied when the object is constructed remain in effect; do not assume a later call will replace them.
#1 Best Overall
How do I read CSV headers in Ruby?
Set headers: true or headers: :first_row to treat the first record as the header row. Subsequent records can then be returned as CSV::Row objects, so fields can be accessed by header name instead of only by index.
require "csv"
CSV.parse("name,agenAda,36nGrace,28n", headers: true).each do |row|
puts "#{row["name"]}: #{row["age"]}"
end
Turning headers on changes the data shape: code written for arrays should be changed to use header-aware access. You can also supply header names yourself, or pass a string that represents a header row.
rows = CSV.parse(
"Ada,36nGrace,28n",
headers: ["name", "age"]
)
rows.each { |row| puts row["name"] }
Header converters are separate from field converters. Use a header converter when names need normalization, and a field converter when values such as numbers or dates need transformation.
Rank #2
What are Ruby CSV’s default options?
| Option | Documented default | What it controls |
|---|---|---|
col_sep |
"," |
Column separator |
row_sep |
:auto |
Record separator; automatic detection for common line endings |
quote_char |
'"' |
Character used to quote fields |
headers |
false |
Whether the first record or supplied names define headers |
converters |
nil |
Field conversion rules |
skip_blanks |
false |
Whether blank rows are skipped |
liberal_parsing |
false |
Whether selected non-compliant input is tolerated |
force_quotes |
false |
Whether generated fields are always quoted |
quote_empty |
true |
Whether empty generated fields are quoted |
These are the defaults documented for the Ruby 3.3 CSV API and CSV gem 3.3.2 references. Set an option explicitly when the producer or consumer requires a different format.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchHow do I change the column separator?
Pass col_sep with the delimiter used by the file. This is common for tab-separated data and semicolon-delimited exports.
text = "nametagenAdat36n"
rows = CSV.parse(text, col_sep: "t")
CSV.open("report.csv", "wb", col_sep: ";") do |csv|
csv << ["name", "age"]
csv << ["Ada", 36]
end
The separator string is transcoded into the data’s encoding for use. A different delimiter does not repair an otherwise malformed file; confirm the file’s structure and encoding first.
Rank #3
How do I handle different line endings?
row_sep: :auto detects common line-ending conventions when parsing. If the format is known, or a downstream system requires a specific convention, provide an explicit separator for parsing or generation.
rows = CSV.parse(text, row_sep: "rn")
CSV.generate(row_sep: "rn") do |csv|
csv << ["name", "age"]
csv << ["Ada", 36]
end
Use an explicit value when you control the output contract. Do not treat row_sep as a substitute for investigating mixed, truncated, or otherwise invalid input.
How do I convert CSV fields to numbers?
Without converters, parsed fields are strings. The :numeric converter turns numeric-looking values into integers or floating-point values. Converters are applied to fields, while header converters handle header names independently.
Rank #4
rows = CSV.parse(
"name,age,scorenAda,36,98.5n",
headers: true,
converters: :numeric
)
row = rows.first
p row["age"] # => 36
p row["score"] # => 98.5
Choose converters only when that transformation matches the application’s data contract. Keeping raw strings can be preferable when formatting, leading zeroes, or exact textual values matter.
How does Ruby CSV handle character encodings?
CSV operates in the encoding of its input String or IO and returns strings in that encoding. It does not transcode data automatically. Custom separators and quote characters must be compatible with the data encoding.
File.open("legacy.csv", "r:Windows-1252:UTF-8") do |io|
CSV.foreach(io, headers: true) do |row|
puts row["name"]
end
end
The file-opening mode above asks Ruby to transcode from Windows-1252 to UTF-8 before CSV parses it. Confirm the source encoding and the exact Ruby/CSV runtime in your deployment; choosing the wrong source encoding can corrupt characters before parsing begins.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Best Value
How should I handle blank lines, comments, and malformed data?
Blank records
Blank rows are retained by default. Set skip_blanks: true when empty records have no meaning in the input format.
Comment lines
CSV parsing supports comment-line skipping. Configure the comment option only when the producer’s format defines comment lines; otherwise a line that looks like a comment may contain real data.
Non-compliant quoting
liberal_parsing: true is an explicit accommodation for inputs that do not follow strict CSV quoting rules. It helps a parser accept selected irregular forms, but it does not validate or repair the source. Keep the strict default when rejecting malformed data is important.
Large fields
The Ruby 3.3 reference marks field_size_limit as deprecated since 3.2.3 and points to max_field_size instead. Use the option documented for the CSV gem version deployed by your application, and treat limits as a defensive resource policy rather than a way to fix bad structure.
Free tools Windows power users keep installed
One-click scans. No signup required.
How do I generate CSV output?
Use CSV.generate for a string and CSV.open or a CSV object for a file or other IO. Arrays are written as records; values that contain the separator, quote character, or a row separator are quoted as required.
csv_text = CSV.generate do |csv|
csv << ["name", "note"]
csv << ["Ada", "Uses, commas"]
end
CSV.open("people.csv", "wb", force_quotes: true) do |csv|
csv << ["name", "age"]
csv << ["Ada", 36]
end
force_quotes: true quotes every generated field. The documented generation default is false; quote_empty defaults to true. Set row_sep, col_sep, and quote_char to match the receiving system rather than relying on defaults.
Quick Recap
A practical configuration checklist
- Identify whether the source is a String, a seekable IO, or a large stream.
- Confirm the column separator and quote character from the actual file.
- Let
row_sep: :autohandle ordinary mixed-platform line endings, or set an explicit separator for a fixed contract. - Decide whether the first row is a header and update access code for
CSV::Rowrecords when it is. - Choose field and header converters independently.
- Decide whether blank rows and comment lines are meaningful.
- Keep strict parsing unless the producer’s non-compliant format requires
liberal_parsing. - Verify the input encoding before selecting custom separators, quote characters, or transcoding modes.
- For output, specify the separators, quoting policy, and encoding expected by the consumer.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

