Skip to the content.

ZLight CSV

Gem Version License: MIT

A fast CSV parser for Ruby, powered by Rust.

Why ZLight?

Ruby’s built-in CSV library is slow. ZLight parses CSV files up to 30x faster by using Rust under the hood.

Benchmark Results

Parsing with headers and numeric conversion (Apple M1):

Dataset Ruby CSV ZLight Speedup
1K rows 12.6ms 0.3ms 42x faster
10K rows 133ms 4.4ms 30x faster
100K rows 1,458ms 78ms 19x faster

File reading comparison (100K rows):

Method Ruby CSV ZLight Speedup
Read all 1,596ms 58ms 27x faster
Streaming 1,104ms 74ms 15x faster

Installation

gem 'zlight_csv'

No Rust toolchain required — prebuilt binaries are available for Linux, macOS, and Windows.

Usage

require 'zlight_csv'

# Parse a CSV string
data = ZLight.parse("name,age\nAlice,30\nBob,25")
# => [{:name=>"Alice", :age=>"30"}, {:name=>"Bob", :age=>"25"}]

# With automatic numeric conversion
data = ZLight.parse(csv_string, converters: :numeric)
# => [{:name=>"Alice", :age=>30}, {:name=>"Bob", :age=>25}]

# Read from a file
data = ZLight.read("users.csv")

# Iterate over rows
ZLight.foreach(csv_string) do |row|
  puts row[:name]
end

Streaming Large Files

For large files, use streaming to process rows one at a time without loading everything into memory:

# Stream from a file (auto-closes when block exits)
ZLight.open("large_file.csv") do |reader|
  reader.each do |row|
    process(row)
  end
end

# Lazy enumeration — stop early without loading remaining rows
ZLight.open("huge_file.csv", converters: :numeric) do |reader|
  high_scores = reader.lazy
                      .select { |row| row[:score] > 90 }
                      .first(100)
end

# Manual control
reader = ZLight.stream_file("data.csv")
while row = reader.next_row
  break if row[:id] > 1000
  process(row)
end
reader.close

Streaming is especially efficient for partial reads:

Finding first 100 rows from 100K dataset:

Ruby CSV (full parse):  1,696ms
ZLight.parse (full):       73ms
ZLight.stream (lazy):     0.3ms  ← 5,600x faster!

Writing CSV

# Generate CSV string from array of hashes
csv_string = ZLight.generate([
  { name: "Alice", age: 30 },
  { name: "Bob", age: 25 }
])
# => "name,age\nAlice,30\nBob,25\n"

# Generate from array of arrays (no headers)
csv_string = ZLight.generate([
  ["Alice", 30],
  ["Bob", 25]
], headers: false)

# Write directly to a file
ZLight.write("output.csv", data)

# Force-quote all fields
ZLight.write("output.csv", data, force_quotes: true)

API Reference

Module Methods (Reading)

ZLight.parse(csv_string, **options) → Array

Parse a CSV string and return an Array of Hashes (with headers) or Arrays (without headers).

ZLight.parse("name,age\nAlice,30")
# => [{:name=>"Alice", :age=>"30"}]

ZLight.parse("Alice,30\nBob,25", headers: false)
# => [["Alice", "30"], ["Bob", "25"]]

ZLight.read(path, **options) → Array

Read and parse a CSV file. Accepts the same options as parse.

ZLight.read("users.csv", converters: :numeric)

ZLight.foreach(csv_string, **options, &block) → Enumerator or nil

Iterate over rows. Returns an Enumerator if no block is given.

ZLight.foreach(csv_string) { |row| puts row[:name] }

# Without block, returns Enumerator
ZLight.foreach(csv_string).map { |row| row[:name].upcase }

ZLight.stream(csv_string, **options) → StreamReader

Create a streaming reader from a string.

reader = ZLight.stream(csv_string)
row = reader.next_row
reader.close

ZLight.stream_file(path, **options) → StreamReader

Create a streaming reader from a file.

reader = ZLight.stream_file("large.csv")
reader.each { |row| process(row) }
reader.close

ZLight.open(path, **options, &block) → Object

Stream a file with auto-close. Works like File.open.

ZLight.open("data.csv") do |reader|
  reader.each { |row| process(row) }
end

Module Methods (Writing)

ZLight.generate(rows, **options) → String

Generate a CSV string from an Array of Hashes or Arrays.

ZLight.generate([{ name: "Alice", age: 30 }])
# => "name,age\nAlice,30\n"

ZLight.generate([["Alice", 30]], headers: false)
# => "Alice,30\n"

ZLight.write(path, rows, **options) → Integer

Write CSV data to a file. Returns the number of bytes written.

ZLight.write("output.csv", data)
ZLight.write("output.csv", data, force_quotes: true, col_sep: ";")

StreamReader Instance Methods

StreamReader includes Enumerable, providing access to map, select, find, lazy, and other enumerable methods.

Method Description
#next_row Read and return the next row (Hash or Array), or nil at EOF
#each(&block) Iterate over all rows; returns Enumerator if no block given
#headers Return headers as Array of Symbols, or nil if headers disabled
#close Close the reader and release resources
#closed? Returns true if the reader is closed
#eof? Returns true if the reader has reached end of file
reader = ZLight.stream_file("data.csv", converters: :numeric)

# Manual iteration
while row = reader.next_row
  break if row[:id] > 100
end

# Lazy enumeration for partial reads
first_ten = reader.lazy.first(10)

reader.close

Options

Reading Options

Option Default Description
headers true Use first row as headers (returns Hashes). Set false for Arrays.
converters nil Set to :numeric to auto-convert numeric strings to Integer/Float
col_sep "," Column separator ("\t" for TSV, ";" for European CSV)
quote_char " Quote character for fields containing separators or newlines
flexible true Allow rows with varying column counts

Writing Options

Option Default Description
headers true Write header row when input is Array of Hashes
col_sep "," Column separator
quote_char " Quote character
force_quotes false Quote all fields, even if not required

Error Classes

All errors inherit from ZLight::Error.

Class Description
ZLight::Error Base error class for all ZLight errors
ZLight::ParseError Raised when CSV is malformed
ZLight::EncodingError Raised when headers contain invalid UTF-8
ZLight::StreamClosedError Raised when operating on a closed StreamReader
begin
  ZLight.parse(malformed_csv)
rescue ZLight::ParseError => e
  puts "Failed to parse: #{e.message}"
end

Constants

Constant Description
ZLight::VERSION Gem version string (e.g., "1.0.0")
puts ZLight::VERSION
# => "1.0.0"

Author

Saad Chaudhary (aka Zaidan Chaudhary)

Requirements

License

MIT — RubyGems