Class: CSV

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/csv.rb,
lib/csv/row.rb,
lib/csv/table.rb,
lib/csv/parser.rb,
lib/csv/writer.rb,
lib/csv/version.rb,
lib/csv/fields_converter.rb,
lib/csv/input_record_separator.rb

Overview

CSV

CSV Data

CSV (comma-separated values) data is a text representation of a table:

  • A row separator delimits table rows. A common row separator is the newline character "\n".

  • A column separator delimits fields in a row. A common column separator is the comma character ",".

This CSV String, with row separator "\n" and column separator ",", has three rows and two columns:

"foo,0\nbar,1\nbaz,2\n"

Despite the name CSV, a CSV representation can use different separators.

For more about tables, see the Wikipedia article “Table (information)”, especially its section “Simple table

Class CSV

Class CSV provides methods for:

  • Parsing CSV data from a String object, a File (via its file path), or an IO object.

  • Generating CSV data to a String object.

To make CSV available:

require 'csv'

All examples here assume that this has been done.

Keeping It Simple

A CSV object has dozens of instance methods that offer fine-grained control of parsing and generating CSV data. For many needs, though, simpler approaches will do.

This section summarizes the singleton methods in CSV that allow you to parse and generate without explicitly creating CSV objects. For details, follow the links.

Simple Parsing

Parsing methods commonly return either of:

  • An Array of Arrays of Strings:

    • The outer Array is the entire “table”.

    • Each inner Array is a row.

    • Each String is a field.

  • A CSV::Table object. For details, see CSV with Headers.

Parsing a String

The input to be parsed can be a string:

string = "foo,0\nbar,1\nbaz,2\n"

Method CSV.parse returns the entire CSV data:

CSV.parse(string) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

Method CSV.parse_line returns only the first row:

CSV.parse_line(string) # => ["foo", "0"]

CSV extends class String with instance method String#parse_csv, which also returns only the first row:

string.parse_csv # => ["foo", "0"]

Parsing Via a File Path

The input to be parsed can be in a file:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)

Method CSV.read returns the entire CSV data:

CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

Method CSV.foreach iterates, passing each row to the given block:

CSV.foreach(path) do |row|
  p row
end

Output:

["foo", "0"]
["bar", "1"]
["baz", "2"]

Method CSV.table returns the entire CSV data as a CSV::Table object:

CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:3>

Parsing from an Open IO Stream

The input to be parsed can be in an open IO stream:

Method CSV.read returns the entire CSV data:

File.open(path) do |file|
  CSV.read(file)
end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

As does method CSV.parse:

File.open(path) do |file|
  CSV.parse(file)
end # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

Method CSV.parse_line returns only the first row:

File.open(path) do |file|
 CSV.parse_line(file)
end # => ["foo", "0"]

Method CSV.foreach iterates, passing each row to the given block:

File.open(path) do |file|
  CSV.foreach(file) do |row|
    p row
  end
end

Output:

["foo", "0"]
["bar", "1"]
["baz", "2"]

Method CSV.table returns the entire CSV data as a CSV::Table object:

File.open(path) do |file|
  CSV.table(file)
end # => #<CSV::Table mode:col_or_row row_count:3>

Simple Generating

Method CSV.generate returns a String; this example uses method CSV#<< to append the rows that are to be generated:

output_string = CSV.generate do |csv|
  csv << ['foo', 0]
  csv << ['bar', 1]
  csv << ['baz', 2]
end
output_string # => "foo,0\nbar,1\nbaz,2\n"

Method CSV.generate_line returns a String containing the single row constructed from an Array:

CSV.generate_line(['foo', '0']) # => "foo,0\n"

CSV extends class Array with instance method Array#to_csv, which forms an Array into a String:

['foo', '0'].to_csv # => "foo,0\n"

“Filtering” CSV

Method CSV.filter provides a Unix-style filter for CSV data. The input data is processed to form the output data:

in_string = "foo,0\nbar,1\nbaz,2\n"
out_string = ''
CSV.filter(in_string, out_string) do |row|
  row[0] = row[0].upcase
  row[1] *= 4
end
out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"

CSV Objects

There are three ways to create a CSV object:

  • Method CSV.new returns a new CSV object.

  • Method CSV.instance returns a new or cached CSV object.

  • Method CSV() also returns a new or cached CSV object.

Instance Methods

CSV has three groups of instance methods:

  • Its own internally defined instance methods.

  • Methods included by module Enumerable.

  • Methods delegated to class IO. See below.

Delegated Methods

For convenience, a CSV object will delegate to many methods in class IO. (A few have wrapper “guard code” in CSV.) You may call:

  • IO#binmode

  • #binmode?

  • IO#close

  • IO#close_read

  • IO#close_write

  • IO#closed?

  • #eof

  • #eof?

  • IO#external_encoding

  • IO#fcntl

  • IO#fileno

  • #flock

  • IO#flush

  • IO#fsync

  • IO#internal_encoding

  • #ioctl

  • IO#isatty

  • #path

  • IO#pid

  • IO#pos

  • IO#pos=

  • IO#reopen

  • #rewind

  • IO#seek

  • #stat

  • IO#string

  • IO#sync

  • IO#sync=

  • IO#tell

  • #to_i

  • #to_io

  • IO#truncate

  • IO#tty?

Options

The default values for options are:

DEFAULT_OPTIONS = {
  # For both parsing and generating.
  col_sep:            ",",
  row_sep:            :auto,
  quote_char:         '"',
  # For parsing.
  field_size_limit:   nil,
  converters:         nil,
  unconverted_fields: nil,
  headers:            false,
  return_headers:     false,
  header_converters:  nil,
  skip_blanks:        false,
  skip_lines:         nil,
  liberal_parsing:    false,
  nil_value:          nil,
  empty_value:        "",
  strip:              false,
  # For generating.
  write_headers:      nil,
  quote_empty:        true,
  force_quotes:       false,
  write_converters:   nil,
  write_nil_value:    nil,
  write_empty_value:  "",
}

Options for Parsing

Options for parsing, described in detail below, include:

  • row_sep: Specifies the row separator; used to delimit rows.

  • col_sep: Specifies the column separator; used to delimit fields.

  • quote_char: Specifies the quote character; used to quote fields.

  • field_size_limit: Specifies the maximum field size + 1 allowed. Deprecated since 3.2.3. Use max_field_size instead.

  • max_field_size: Specifies the maximum field size allowed.

  • converters: Specifies the field converters to be used.

  • unconverted_fields: Specifies whether unconverted fields are to be available.

  • headers: Specifies whether data contains headers, or specifies the headers themselves.

  • return_headers: Specifies whether headers are to be returned.

  • header_converters: Specifies the header converters to be used.

  • skip_blanks: Specifies whether blanks lines are to be ignored.

  • skip_lines: Specifies how comments lines are to be recognized.

  • strip: Specifies whether leading and trailing whitespace are to be stripped from fields. This must be compatible with col_sep; if it is not, then an ArgumentError exception will be raised.

  • liberal_parsing: Specifies whether CSV should attempt to parse non-compliant data.

  • nil_value: Specifies the object that is to be substituted for each null (no-text) field.

  • empty_value: Specifies the object that is to be substituted for each empty field.

:include: ../doc/csv/options/common/row_sep.rdoc

:include: ../doc/csv/options/common/col_sep.rdoc

:include: ../doc/csv/options/common/quote_char.rdoc

:include: ../doc/csv/options/parsing/field_size_limit.rdoc

:include: ../doc/csv/options/parsing/converters.rdoc

:include: ../doc/csv/options/parsing/unconverted_fields.rdoc

:include: ../doc/csv/options/parsing/headers.rdoc

:include: ../doc/csv/options/parsing/return_headers.rdoc

:include: ../doc/csv/options/parsing/header_converters.rdoc

:include: ../doc/csv/options/parsing/skip_blanks.rdoc

:include: ../doc/csv/options/parsing/skip_lines.rdoc

:include: ../doc/csv/options/parsing/strip.rdoc

:include: ../doc/csv/options/parsing/liberal_parsing.rdoc

:include: ../doc/csv/options/parsing/nil_value.rdoc

:include: ../doc/csv/options/parsing/empty_value.rdoc

Options for Generating

Options for generating, described in detail below, include:

  • row_sep: Specifies the row separator; used to delimit rows.

  • col_sep: Specifies the column separator; used to delimit fields.

  • quote_char: Specifies the quote character; used to quote fields.

  • write_headers: Specifies whether headers are to be written.

  • force_quotes: Specifies whether each output field is to be quoted.

  • quote_empty: Specifies whether each empty output field is to be quoted.

  • write_converters: Specifies the field converters to be used in writing.

  • write_nil_value: Specifies the object that is to be substituted for each nil-valued field.

  • write_empty_value: Specifies the object that is to be substituted for each empty field.

:include: ../doc/csv/options/common/row_sep.rdoc

:include: ../doc/csv/options/common/col_sep.rdoc

:include: ../doc/csv/options/common/quote_char.rdoc

:include: ../doc/csv/options/generating/write_headers.rdoc

:include: ../doc/csv/options/generating/force_quotes.rdoc

:include: ../doc/csv/options/generating/quote_empty.rdoc

:include: ../doc/csv/options/generating/write_converters.rdoc

:include: ../doc/csv/options/generating/write_nil_value.rdoc

:include: ../doc/csv/options/generating/write_empty_value.rdoc

CSV with Headers

CSV allows to specify column names of CSV file, whether they are in data, or provided separately. If headers are specified, reading methods return an instance of CSV::Table, consisting of CSV::Row.

# Headers are part of data
data = CSV.parse(<<~ROWS, headers: true)
  Name,Department,Salary
  Bob,Engineering,1000
  Jane,Sales,2000
  John,Management,5000
ROWS

data.class      #=> CSV::Table
data.first      #=> #<CSV::Row "Name":"Bob" "Department":"Engineering" "Salary":"1000">
data.first.to_h #=> {"Name"=>"Bob", "Department"=>"Engineering", "Salary"=>"1000"}

# Headers provided by developer
data = CSV.parse('Bob,Engineering,1000', headers: %i[name department salary])
data.first      #=> #<CSV::Row name:"Bob" department:"Engineering" salary:"1000">

Converters

By default, each value (field or header) parsed by CSV is formed into a String. You can use a field converter or header converter to intercept and modify the parsed values:

Also by default, each value to be written during generation is written ‘as-is’. You can use a write converter to modify values before writing.

Specifying Converters

You can specify converters for parsing or generating in the options argument to various CSV methods:

  • Option converters for converting parsed field values.

  • Option header_converters for converting parsed header values.

  • Option write_converters for converting values to be written (generated).

There are three forms for specifying converters:

  • A converter proc: executable code to be used for conversion.

  • A converter name: the name of a stored converter.

  • A converter list: an array of converter procs, converter names, and converter lists.

Converter Procs

This converter proc, strip_converter, accepts a value field and returns field.strip:

strip_converter = proc {|field| field.strip }

In this call to CSV.parse, the keyword argument converters: string_converter specifies that:

  • Proc string_converter is to be called for each parsed field.

  • The converter’s return value is to replace the field value.

Example:

string = " foo , 0 \n bar , 1 \n baz , 2 \n"
array = CSV.parse(string, converters: strip_converter)
array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

A converter proc can receive a second argument, field_info, that contains details about the field. This modified strip_converter displays its arguments:

strip_converter = proc do |field, field_info|
  p [field, field_info]
  field.strip
end
string = " foo , 0 \n bar , 1 \n baz , 2 \n"
array = CSV.parse(string, converters: strip_converter)
array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

Output:

[" foo ", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
[" 0 ", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
[" bar ", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
[" 1 ", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
[" baz ", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
[" 2 ", #<struct CSV::FieldInfo index=1, line=3, header=nil>]

Each CSV::FieldInfo object shows:

  • The 0-based field index.

  • The 1-based line index.

  • The field header, if any.

Stored Converters

A converter may be given a name and stored in a structure where the parsing methods can find it by name.

The storage structure for field converters is the Hash CSV::Converters. It has several built-in converter procs:

  • :integer: converts each String-embedded integer into a true Integer.

  • :float: converts each String-embedded float into a true Float.

  • :date: converts each String-embedded date into a true Date.

  • :date_time: converts each String-embedded date-time into a true DateTime

  • :time: converts each String-embedded time into a true Time

. This example creates a converter proc, then stores it:

strip_converter = proc {|field| field.strip }
CSV::Converters[:strip] = strip_converter

Then the parsing method call can refer to the converter by its name, :strip:

string = " foo , 0 \n bar , 1 \n baz , 2 \n"
array = CSV.parse(string, converters: :strip)
array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

The storage structure for header converters is the Hash CSV::HeaderConverters, which works in the same way. It also has built-in converter procs:

  • :downcase: Downcases each header.

  • :symbol: Converts each header to a Symbol.

There is no such storage structure for write headers.

In order for the parsing methods to access stored converters in non-main-Ractors, the storage structure must be made shareable first. Therefore, Ractor.make_shareable(CSV::Converters) and Ractor.make_shareable(CSV::HeaderConverters) must be called before the creation of Ractors that use the converters stored in these structures. (Since making the storage structures shareable involves freezing them, any custom converters that are to be used must be added first.)

Converter Lists

A converter list is an Array that may include any assortment of:

  • Converter procs.

  • Names of stored converters.

  • Nested converter lists.

Examples:

numeric_converters = [:integer, :float]
date_converters = [:date, :date_time]
[numeric_converters, strip_converter]
[strip_converter, date_converters, :float]

Like a converter proc, a converter list may be named and stored in either CSV::Converters or CSV::HeaderConverters:

CSV::Converters[:custom] = [strip_converter, date_converters, :float]
CSV::HeaderConverters[:custom] = [:downcase, :symbol]

There are two built-in converter lists:

CSV::Converters[:numeric] # => [:integer, :float]
CSV::Converters[:all] # => [:date_time, :numeric]

Field Converters

With no conversion, all parsed fields in all rows become Strings:

string = "foo,0\nbar,1\nbaz,2\n"
ary = CSV.parse(string)
ary # => # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

When you specify a field converter, each parsed field is passed to the converter; its return value becomes the stored value for the field. A converter might, for example, convert an integer embedded in a String into a true Integer. (In fact, that’s what built-in field converter :integer does.)

There are three ways to use field converters.

  • Using option converters with a parsing method:

    ary = CSV.parse(string, converters: :integer)
    ary # => [0, 1, 2] # => [["foo", 0], ["bar", 1], ["baz", 2]]
    
  • Using option converters with a new CSV instance:

    csv = CSV.new(string, converters: :integer)
    # Field converters in effect:
    csv.converters # => [:integer]
    csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
    
  • Using method #convert to add a field converter to a CSV instance:

    csv = CSV.new(string)
    # Add a converter.
    csv.convert(:integer)
    csv.converters # => [:integer]
    csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]
    

Installing a field converter does not affect already-read rows:

csv = CSV.new(string)
csv.shift # => ["foo", "0"]
# Add a converter.
csv.convert(:integer)
csv.converters # => [:integer]
csv.read # => [["bar", 1], ["baz", 2]]

There are additional built-in converters, and custom converters are also supported.

Built-In Field Converters

The built-in field converters are in Hash CSV::Converters:

  • Each key is a field converter name.

  • Each value is one of:

    • A Proc field converter.

    • An Array of field converter names.

Display:

CSV::Converters.each_pair do |name, value|
  if value.kind_of?(Proc)
    p [name, value.class]
  else
    p [name, value]
  end
end

Output:

[:integer, Proc]
[:float, Proc]
[:numeric, [:integer, :float]]
[:date, Proc]
[:date_time, Proc]
[:time, Proc]
[:all, [:date_time, :numeric]]

Each of these converters transcodes values to UTF-8 before attempting conversion. If a value cannot be transcoded to UTF-8 the conversion will fail and the value will remain unconverted.

Converter :integer converts each field that Integer() accepts:

data = '0,1,2,x'
# Without the converter
csv = CSV.parse_line(data)
csv # => ["0", "1", "2", "x"]
# With the converter
csv = CSV.parse_line(data, converters: :integer)
csv # => [0, 1, 2, "x"]

Converter :float converts each field that Float() accepts:

data = '1.0,3.14159,x'
# Without the converter
csv = CSV.parse_line(data)
csv # => ["1.0", "3.14159", "x"]
# With the converter
csv = CSV.parse_line(data, converters: :float)
csv # => [1.0, 3.14159, "x"]

Converter :numeric converts with both :integer and :float..

Converter :date converts each field that Date::parse accepts:

data = '2001-02-03,x'
# Without the converter
csv = CSV.parse_line(data)
csv # => ["2001-02-03", "x"]
# With the converter
csv = CSV.parse_line(data, converters: :date)
csv # => [#<Date: 2001-02-03 ((2451944j,0s,0n),+0s,2299161j)>, "x"]

Converter :date_time converts each field that DateTime::parse accepts:

data = '2020-05-07T14:59:00-05:00,x'
# Without the converter
csv = CSV.parse_line(data)
csv # => ["2020-05-07T14:59:00-05:00", "x"]
# With the converter
csv = CSV.parse_line(data, converters: :date_time)
csv # => [#<DateTime: 2020-05-07T14:59:00-05:00 ((2458977j,71940s,0n),-18000s,2299161j)>, "x"]

Converter time converts each field that Time::parse accepts:

data = '2020-05-07T14:59:00-05:00,x'
# Without the converter
csv = CSV.parse_line(data)
csv # => ["2020-05-07T14:59:00-05:00", "x"]
# With the converter
csv = CSV.parse_line(data, converters: :time)
csv # => [2020-05-07 14:59:00 -0500, "x"]

Converter :numeric converts with both :date_time and :numeric..

As seen above, method #convert adds converters to a CSV instance, and method #converters returns an Array of the converters in effect:

csv = CSV.new('0,1,2')
csv.converters # => []
csv.convert(:integer)
csv.converters # => [:integer]
csv.convert(:date)
csv.converters # => [:integer, :date]
Custom Field Converters

You can define a custom field converter:

strip_converter = proc {|field| field.strip }
string = " foo , 0 \n bar , 1 \n baz , 2 \n"
array = CSV.parse(string, converters: strip_converter)
array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

You can register the converter in Converters Hash, which allows you to refer to it by name:

CSV::Converters[:strip] = strip_converter
string = " foo , 0 \n bar , 1 \n baz , 2 \n"
array = CSV.parse(string, converters: :strip)
array # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

Header Converters

Header converters operate only on headers (and not on other rows).

There are three ways to use header converters; these examples use built-in header converter :downcase, which downcases each parsed header.

  • Option header_converters with a singleton parsing method:

    string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
    tbl = CSV.parse(string, headers: true, header_converters: :downcase)
    tbl.class # => CSV::Table
    tbl.headers # => ["name", "count"]
    
  • Option header_converters with a new CSV instance:

    csv = CSV.new(string, header_converters: :downcase)
    # Header converters in effect:
    csv.header_converters # => [:downcase]
    tbl = CSV.parse(string, headers: true)
    tbl.headers # => ["Name", "Count"]
    
  • Method #header_convert adds a header converter to a CSV instance:

    csv = CSV.new(string)
    # Add a header converter.
    csv.header_convert(:downcase)
    csv.header_converters # => [:downcase]
    tbl = CSV.parse(string, headers: true)
    tbl.headers # => ["Name", "Count"]
    
Built-In Header Converters

The built-in header converters are in Hash CSV::HeaderConverters. The keys there are the names of the converters:

CSV::HeaderConverters.keys # => [:downcase, :symbol]

Converter :downcase converts each header by downcasing it:

string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
tbl = CSV.parse(string, headers: true, header_converters: :downcase)
tbl.class # => CSV::Table
tbl.headers # => ["name", "count"]

Converter :symbol converts each header by making it into a Symbol:

string = "Name,Count\nFoo,0\n,Bar,1\nBaz,2"
tbl = CSV.parse(string, headers: true, header_converters: :symbol)
tbl.headers # => [:name, :count]

Details:

  • Strips leading and trailing whitespace.

  • Downcases the header.

  • Replaces embedded spaces with underscores.

  • Removes non-word characters.

  • Makes the string into a Symbol.

Custom Header Converters

You can define a custom header converter:

upcase_converter = proc {|header| header.upcase }
string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
table = CSV.parse(string, headers: true, header_converters: upcase_converter)
table # => #<CSV::Table mode:col_or_row row_count:4>
table.headers # => ["NAME", "VALUE"]

You can register the converter in HeaderConverters Hash, which allows you to refer to it by name:

CSV::HeaderConverters[:upcase] = upcase_converter
table = CSV.parse(string, headers: true, header_converters: :upcase)
table # => #<CSV::Table mode:col_or_row row_count:4>
table.headers # => ["NAME", "VALUE"]
Write Converters

When you specify a write converter for generating CSV, each field to be written is passed to the converter; its return value becomes the new value for the field. A converter might, for example, strip whitespace from a field.

Using no write converter (all fields unmodified):

output_string = CSV.generate do |csv|
  csv << [' foo ', 0]
  csv << [' bar ', 1]
  csv << [' baz ', 2]
end
output_string # => " foo ,0\n bar ,1\n baz ,2\n"

Using option write_converters with two custom write converters:

strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field }
upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field }
write_converters = [strip_converter, upcase_converter]
output_string = CSV.generate(write_converters: write_converters) do |csv|
  csv << [' foo ', 0]
  csv << [' bar ', 1]
  csv << [' baz ', 2]
end
output_string # => "FOO,0\nBAR,1\nBAZ,2\n"

Character Encodings (M17n or Multilingualization)

This new CSV parser is m17n savvy. The parser works in the Encoding of the IO or String object being read from or written to. Your data is never transcoded (unless you ask Ruby to transcode it for you) and will literally be parsed in the Encoding it is in. Thus CSV will return Arrays or Rows of Strings in the Encoding of your data. This is accomplished by transcoding the parser itself into your Encoding.

Some transcoding must take place, of course, to accomplish this multiencoding support. For example, :col_sep, :row_sep, and :quote_char must be transcoded to match your data. Hopefully this makes the entire process feel transparent, since CSV’s defaults should just magically work for your data. However, you can set these values manually in the target Encoding to avoid the translation.

It’s also important to note that while all of CSV’s core parser is now Encoding agnostic, some features are not. For example, the built-in converters will try to transcode data to UTF-8 before making conversions. Again, you can provide custom converters that are aware of your Encodings to avoid this translation. It’s just too hard for me to support native conversions in all of Ruby’s Encodings.

Anyway, the practical side of this is simple: make sure IO and String objects passed into CSV have the proper Encoding set and everything should just work. CSV methods that allow you to open IO objects (CSV::foreach(), CSV::open(), CSV::read(), and CSV::readlines()) do allow you to specify the Encoding.

One minor exception comes when generating CSV into a String with an Encoding that is not ASCII compatible. There’s no existing data for CSV to use to prepare itself and thus you will probably need to manually specify the desired Encoding for most of those cases. It will try to guess using the fields in a row of output though, when using CSV::generate_line() or Array#to_csv().

I try to point out any other Encoding issues in the documentation of methods as they come up.

This has been tested to the best of my ability with all non-“dummy” Encodings Ruby ships with. However, it is brave new code and may have some bugs. Please feel free to report any issues you find with it.

Direct Known Subclasses

TSV

Defined Under Namespace

Modules: InputRecordSeparator Classes: FieldInfo, FieldsConverter, InvalidEncodingError, MalformedCSVError, Parser, Row, TSV, Table, Writer

Constant Summary collapse

DateMatcher =

A Regexp used to find and convert some common Date formats.

/ \A(?: (\w+,?\s+)?\w+\s+\d{1,2},?\s+\d{2,4} |
\d{4}-\d{2}-\d{2} )\z /x
DateTimeMatcher =

A Regexp used to find and convert some common (Date)Time formats.

/ \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} |
    # ISO-8601 and RFC-3339 (space instead of T) recognized by (Date)Time.parse
    \d{4}-\d{2}-\d{2}
      (?:[T\s]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:[+-]\d{2}(?::\d{2})|Z)?)?)?
)\z /x
ConverterEncoding =

The encoding used by all converters.

Encoding.find("UTF-8")
Converters =

A Hash containing the names and Procs for the built-in field converters. See Built-In Field Converters.

This Hash is intentionally left unfrozen, and may be extended with custom field converters. See Custom Field Converters.

{
  integer:   lambda { |f|
    Integer(f.encode(ConverterEncoding)) rescue f
  },
  float:     lambda { |f|
    Float(f.encode(ConverterEncoding)) rescue f
  },
  numeric:   [:integer, :float],
  date:      lambda { |f|
    begin
      e = f.encode(ConverterEncoding)
      e.match?(DateMatcher) ? Date.parse(e) : f
    rescue  # encoding conversion or date parse errors
      f
    end
  },
  date_time: lambda { |f|
    begin
      e = f.encode(ConverterEncoding)
      e.match?(DateTimeMatcher) ? DateTime.parse(e) : f
    rescue  # encoding conversion or date parse errors
      f
    end
  },
  time: lambda { |f|
    begin
      e = f.encode(ConverterEncoding)
      e.match?(DateTimeMatcher) ? Time.parse(e) : f
    rescue  # encoding conversion or parse errors
      f
    end
  },
  all:       [:date_time, :numeric],
}
HeaderConverters =

A Hash containing the names and Procs for the built-in header converters. See Built-In Header Converters.

This Hash is intentionally left unfrozen, and may be extended with custom field converters. See Custom Header Converters.

{
  downcase: lambda { |h| h.encode(ConverterEncoding).downcase },
  symbol:   lambda { |h|
    h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
                                         gsub(/\s+/, "_").to_sym
  },
  symbol_raw: lambda { |h| h.encode(ConverterEncoding).to_sym }
}
DEFAULT_OPTIONS =

Default values for method options.

{
  # For both parsing and generating.
  col_sep:            ",",
  row_sep:            :auto,
  quote_char:         '"',
  # For parsing.
  field_size_limit:   nil,
  max_field_size:     nil,
  converters:         nil,
  unconverted_fields: nil,
  headers:            false,
  return_headers:     false,
  header_converters:  nil,
  skip_blanks:        false,
  skip_lines:         nil,
  liberal_parsing:    false,
  nil_value:          nil,
  empty_value:        "",
  strip:              false,
  # For generating.
  write_headers:      nil,
  quote_empty:        true,
  force_quotes:       false,
  write_converters:   nil,
  write_nil_value:    nil,
  write_empty_value:  "",
}.freeze
VERSION =

The version of the installed library.

"3.3.1"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, col_sep: ",", row_sep: :auto, quote_char: '"', field_size_limit: nil, max_field_size: nil, converters: nil, unconverted_fields: nil, headers: false, return_headers: false, write_headers: nil, header_converters: nil, skip_blanks: false, force_quotes: false, skip_lines: nil, liberal_parsing: false, internal_encoding: nil, external_encoding: nil, encoding: nil, nil_value: nil, empty_value: "", strip: false, quote_empty: true, write_converters: nil, write_nil_value: nil, write_empty_value: "") ⇒ CSV

:call-seq:

CSV.new(string)
CSV.new(io)
CSV.new(string, **options)
CSV.new(io, **options)

Returns the new CSV object created using string or io and the specified options.

  • Argument string should be a String object; it will be put into a new StringIO object positioned at the beginning.

:include: ../doc/csv/arguments/io.rdoc

In addition to the CSV instance methods, several IO methods are delegated. See Delegated Methods.


Create a CSV object from a String object:

csv = CSV.new('foo,0')
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Create a CSV object from a File object:

File.write('t.csv', 'foo,0')
csv = CSV.new(File.open('t.csv'))
csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Raises an exception if the argument is nil:

# Raises ArgumentError (Cannot parse nil as CSV):
CSV.new(nil)

Raises:

  • (ArgumentError)


2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
# File 'lib/csv.rb', line 2034

def initialize(data,
               col_sep: ",",
               row_sep: :auto,
               quote_char: '"',
               field_size_limit: nil,
               max_field_size: nil,
               converters: nil,
               unconverted_fields: nil,
               headers: false,
               return_headers: false,
               write_headers: nil,
               header_converters: nil,
               skip_blanks: false,
               force_quotes: false,
               skip_lines: nil,
               liberal_parsing: false,
               internal_encoding: nil,
               external_encoding: nil,
               encoding: nil,
               nil_value: nil,
               empty_value: "",
               strip: false,
               quote_empty: true,
               write_converters: nil,
               write_nil_value: nil,
               write_empty_value: "")
  raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?

  if data.is_a?(String)
    if encoding
      if encoding.is_a?(String)
        data_external_encoding, data_internal_encoding = encoding.split(":", 2)
        if data_internal_encoding
          data = data.encode(data_internal_encoding, data_external_encoding)
        else
          data = data.dup.force_encoding(data_external_encoding)
        end
      else
        data = data.dup.force_encoding(encoding)
      end
    end
    @io = StringIO.new(data)
  else
    @io = data
  end
  @encoding = determine_encoding(encoding, internal_encoding)

  @base_fields_converter_options = {
    nil_value: nil_value,
    empty_value: empty_value,
  }
  @write_fields_converter_options = {
    nil_value: write_nil_value,
    empty_value: write_empty_value,
  }
  @initial_converters = converters
  @initial_header_converters = header_converters
  @initial_write_converters = write_converters

  if max_field_size.nil? and field_size_limit
    max_field_size = field_size_limit - 1
  end
  @parser_options = {
    column_separator: col_sep,
    row_separator: row_sep,
    quote_character: quote_char,
    max_field_size: max_field_size,
    unconverted_fields: unconverted_fields,
    headers: headers,
    return_headers: return_headers,
    skip_blanks: skip_blanks,
    skip_lines: skip_lines,
    liberal_parsing: liberal_parsing,
    encoding: @encoding,
    nil_value: nil_value,
    empty_value: empty_value,
    strip: strip,
  }
  @parser = nil
  @parser_enumerator = nil
  @eof_error = nil

  @writer_options = {
    encoding: @encoding,
    force_encoding: (not encoding.nil?),
    force_quotes: force_quotes,
    headers: headers,
    write_headers: write_headers,
    column_separator: col_sep,
    row_separator: row_sep,
    quote_character: quote_char,
    quote_empty: quote_empty,
  }

  @writer = nil
  writer if @writer_options[:write_headers]
end

Instance Attribute Details

#encodingObject (readonly)

:call-seq:

csv.encoding -> encoding

Returns the encoding used for parsing and generating; see Character Encodings (M17n or Multilingualization):

CSV.new('').encoding # => #<Encoding:UTF-8>


2327
2328
2329
# File 'lib/csv.rb', line 2327

def encoding
  @encoding
end

Class Method Details

.filter(input = nil, output = nil, **options) ⇒ Object

:call-seq:

filter(in_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
filter(in_string_or_io, out_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
filter(**options) {|row| ... } -> array_of_arrays or csv_table
  • Parses CSV from a source (String, IO stream, or ARGF).

  • Calls the given block with each parsed row:

    • Without headers, each row is an Array.

    • With headers, each row is a CSV::Row.

  • Generates CSV to an output (String, IO stream, or STDOUT).

  • Returns the parsed source:

    • Without headers, an Array of Arrays.

    • With headers, a CSV::Table.

When in_string_or_io is given, but not out_string_or_io, parses from the given in_string_or_io and generates to STDOUT.

String input without headers:

in_string = "foo,0\nbar,1\nbaz,2"
CSV.filter(in_string) do |row|
  row[0].upcase!
  row[1] = - row[1].to_i
end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]

Output (to STDOUT):

FOO,0
BAR,-1
BAZ,-2

String input with headers:

in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
CSV.filter(in_string, headers: true) do |row|
  row[0].upcase!
  row[1] = - row[1].to_i
end # => #<CSV::Table mode:col_or_row row_count:4>

Output (to STDOUT):

Name,Value
FOO,0
BAR,-1
BAZ,-2

IO stream input without headers:

File.write('t.csv', "foo,0\nbar,1\nbaz,2")
File.open('t.csv') do |in_io|
  CSV.filter(in_io) do |row|
    row[0].upcase!
    row[1] = - row[1].to_i
  end
end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]

Output (to STDOUT):

FOO,0
BAR,-1
BAZ,-2

IO stream input with headers:

File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
File.open('t.csv') do |in_io|
  CSV.filter(in_io, headers: true) do |row|
    row[0].upcase!
    row[1] = - row[1].to_i
  end
end # => #<CSV::Table mode:col_or_row row_count:4>

Output (to STDOUT):

Name,Value
FOO,0
BAR,-1
BAZ,-2

When both in_string_or_io and out_string_or_io are given, parses from in_string_or_io and generates to out_string_or_io.

String output without headers:

in_string = "foo,0\nbar,1\nbaz,2"
out_string = ''
CSV.filter(in_string, out_string) do |row|
  row[0].upcase!
  row[1] = - row[1].to_i
end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"

String output with headers:

in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
out_string = ''
CSV.filter(in_string, out_string, headers: true) do |row|
  row[0].upcase!
  row[1] = - row[1].to_i
end # => #<CSV::Table mode:col_or_row row_count:4>
out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"

IO stream output without headers:

in_string = "foo,0\nbar,1\nbaz,2"
File.open('t.csv', 'w') do |out_io|
  CSV.filter(in_string, out_io) do |row|
    row[0].upcase!
    row[1] = - row[1].to_i
  end
end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"

IO stream output with headers:

in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
File.open('t.csv', 'w') do |out_io|
  CSV.filter(in_string, out_io, headers: true) do |row|
    row[0].upcase!
    row[1] = - row[1].to_i
  end
end # => #<CSV::Table mode:col_or_row row_count:4>
File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"

When neither in_string_or_io nor out_string_or_io given, parses from ARGF and generates to STDOUT.

Without headers:

# Put Ruby code into a file.
ruby = <<-EOT
  require 'csv'
  CSV.filter do |row|
    row[0].upcase!
    row[1] = - row[1].to_i
  end
EOT
File.write('t.rb', ruby)
# Put some CSV into a file.
File.write('t.csv', "foo,0\nbar,1\nbaz,2")
# Run the Ruby code with CSV filename as argument.
system(Gem.ruby, "t.rb", "t.csv")

Output (to STDOUT):

FOO,0
BAR,-1
BAZ,-2

With headers:

# Put Ruby code into a file.
ruby = <<-EOT
  require 'csv'
  CSV.filter(headers: true) do |row|
    row[0].upcase!
    row[1] = - row[1].to_i
  end
EOT
File.write('t.rb', ruby)
# Put some CSV into a file.
File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
# Run the Ruby code with CSV filename as argument.
system(Gem.ruby, "t.rb", "t.csv")

Output (to STDOUT):

Name,Value
FOO,0
BAR,-1
BAZ,-2

Arguments:

  • Argument in_string_or_io must be a String or an IO stream.

  • Argument out_string_or_io must be a String or an IO stream.

  • Arguments **options must be keyword options.

However, there are three options that may be used for both parsing and generating: col_sep, quote_char, and row_sep.

Therefore for method filter (and method filter only), there are special options that allow these parsing and generating options to be specified separately:

  • Options input_col_sep and output_col_sep (and their aliases in_col_sep and out_col_sep) specify the column separators for parsing and generating.

  • Options input_quote_char and output_quote_char (and their aliases in_quote_char and out_quote_char) specify the quote characters for parsing and generting.

  • Options input_row_sep and output_row_sep (and their aliases in_row_sep and out_row_sep) specify the row separators for parsing and generating.

Example options (for column separators):

CSV.filter                                    # Default for both parsing and generating.
CSV.filter(in_col_sep: ';')                   # ';' for parsing, default for generating.
CSV.filter(out_col_sep: '|')                  # Default for parsing, '|' for generating.
CSV.filter(in_col_sep: ';', out_col_sep: '|') # ';' for parsing, '|' for generating.

Note that for a special option (e.g., input_col_sep) and its corresponding “regular” option (e.g., col_sep), the two are mutually overriding.

Another example (possibly surprising):

CSV.filter(in_col_sep: ';', col_sep: '|') # '|' for both parsing(!) and generating.


1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
# File 'lib/csv.rb', line 1259

def filter(input=nil, output=nil, **options)
  # parse options for input, output, or both
  in_options, out_options = Hash.new, {row_sep: InputRecordSeparator.value}
  options.each do |key, value|
    case key
    when /\Ain(?:put)?_(.+)\Z/
      in_options[$1.to_sym] = value
    when /\Aout(?:put)?_(.+)\Z/
      out_options[$1.to_sym] = value
    else
      in_options[key]  = value
      out_options[key] = value
    end
  end

  # build input and output wrappers
  input  = new(input  || ARGF, **in_options)
  output = new(output || $stdout, **out_options)

  # process headers
  need_manual_header_output =
    (in_options[:headers] and
     out_options[:headers] == true and
     out_options[:write_headers])
  if need_manual_header_output
    first_row = input.shift
    if first_row
      if first_row.is_a?(Row)
        headers = first_row.headers
        yield headers
        output << headers
      end
      yield first_row
      output << first_row
    end
  end

  # read, yield, write
  input.each do |row|
    yield row
    output << row
  end
end

.foreach(path, mode = "r", **options, &block) ⇒ Object

:call-seq:

foreach(path_or_io, mode='r', **options) {|row| ... )
foreach(path_or_io, mode='r', **options) -> new_enumerator

Calls the block with each row read from source path_or_io.

Path input without headers:

string = "foo,0\nbar,1\nbaz,2\n"
in_path = 't.csv'
File.write(in_path, string)
CSV.foreach(in_path) {|row| p row }

Output:

["foo", "0"]
["bar", "1"]
["baz", "2"]

Path input with headers:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
in_path = 't.csv'
File.write(in_path, string)
CSV.foreach(in_path, headers: true) {|row| p row }

Output:

<CSV::Row "Name":"foo" "Value":"0">
<CSV::Row "Name":"bar" "Value":"1">
<CSV::Row "Name":"baz" "Value":"2">

IO stream input without headers:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
File.open('t.csv') do |in_io|
  CSV.foreach(in_io) {|row| p row }
end

Output:

["foo", "0"]
["bar", "1"]
["baz", "2"]

IO stream input with headers:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
File.open('t.csv') do |in_io|
  CSV.foreach(in_io, headers: true) {|row| p row }
end

Output:

<CSV::Row "Name":"foo" "Value":"0">
<CSV::Row "Name":"bar" "Value":"1">
<CSV::Row "Name":"baz" "Value":"2">

With no block given, returns an Enumerator:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>

Arguments:

  • Argument path_or_io must be a file path or an IO stream.

  • Argument mode, if given, must be a File mode. See Access Modes.

  • Arguments **options must be keyword options. See Options for Parsing.

  • This method optionally accepts an additional :encoding option that you can use to specify the Encoding of the data read from path or io. You must provide this unless your data is in the encoding given by Encoding::default_external. Parsing will use this to determine how to parse the data. You may provide a second Encoding to have the data transcoded as it is read. For example,

    encoding: 'UTF-32BE:UTF-8'
    

    would read UTF-32BE data from the file but transcode it to UTF-8 before parsing.



1389
1390
1391
1392
1393
1394
# File 'lib/csv.rb', line 1389

def foreach(path, mode="r", **options, &block)
  return to_enum(__method__, path, mode, **options) unless block_given?
  open(path, mode, **options) do |csv|
    csv.each(&block)
  end
end

.generate(str = nil, **options) {|csv| ... } ⇒ Object

:call-seq:

generate(csv_string, **options) {|csv| ... }
generate(**options) {|csv| ... }
  • Argument csv_string, if given, must be a String object; defaults to a new empty String.

  • Arguments options, if given, should be generating options. See Options for Generating.


Creates a new CSV object via CSV.new(csv_string, **options); calls the block with the CSV object, which the block may modify; returns the String generated from the CSV object.

Note that a passed String is modified by this method. Pass csv_string.dup if the String must be preserved.

This method has one additional option: :encoding, which sets the base Encoding for the output if no no str is specified. CSV needs this hint if you plan to output non-ASCII compatible data.


Add lines:

input_string = "foo,0\nbar,1\nbaz,2\n"
output_string = CSV.generate(input_string) do |csv|
  csv << ['bat', 3]
  csv << ['bam', 4]
end
output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
input_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
output_string.equal?(input_string) # => true # Same string, modified

Add lines into new string, preserving old string:

input_string = "foo,0\nbar,1\nbaz,2\n"
output_string = CSV.generate(input_string.dup) do |csv|
  csv << ['bat', 3]
  csv << ['bam', 4]
end
output_string # => "foo,0\nbar,1\nbaz,2\nbat,3\nbam,4\n"
input_string # => "foo,0\nbar,1\nbaz,2\n"
output_string.equal?(input_string) # => false # Different strings

Create lines from nothing:

output_string = CSV.generate do |csv|
  csv << ['foo', 0]
  csv << ['bar', 1]
  csv << ['baz', 2]
end
output_string # => "foo,0\nbar,1\nbaz,2\n"

Raises an exception if csv_string is not a String object:

# Raises TypeError (no implicit conversion of Integer into String)
CSV.generate(0)

Yields:

  • (csv)


1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
# File 'lib/csv.rb', line 1455

def generate(str=nil, **options)
  encoding = options[:encoding]
  # add a default empty String, if none was given
  if str
    str = StringIO.new(str)
    str.seek(0, IO::SEEK_END)
    str.set_encoding(encoding) if encoding
  else
    str = +""
    str.force_encoding(encoding) if encoding
  end
  csv = new(str, **options) # wrap
  yield csv         # yield for appending
  csv.string        # return final String
end

.generate_line(row, **options) ⇒ Object

:call-seq:

CSV.generate_line(ary)
CSV.generate_line(ary, **options)

Returns the String created by generating CSV from ary using the specified options.

Argument ary must be an Array.

Special options:

  • Option :row_sep defaults to "\n"> on Ruby 3.0 or later and <tt>$INPUT_RECORD_SEPARATOR ($/) otherwise.:

    $INPUT_RECORD_SEPARATOR # => "\n"
    
  • This method accepts an additional option, :encoding, which sets the base Encoding for the output. This method will try to guess your Encoding from the first non-nil field in row, if possible, but you may need to use this parameter as a backup plan.

For other options, see Options for Generating.


Returns the String generated from an Array:

CSV.generate_line(['foo', '0']) # => "foo,0\n"

Raises an exception if ary is not an Array:

# Raises NoMethodError (undefined method `find' for :foo:Symbol)
CSV.generate_line(:foo)


1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
# File 'lib/csv.rb', line 1503

def generate_line(row, **options)
  options = {row_sep: InputRecordSeparator.value}.merge(options)
  str = +""
  if options[:encoding]
    str.force_encoding(options[:encoding])
  else
    fallback_encoding = nil
    output_encoding = nil
    row.each do |field|
      next unless field.is_a?(String)
      fallback_encoding ||= field.encoding
      next if field.ascii_only?
      output_encoding = field.encoding
      break
    end
    output_encoding ||= fallback_encoding
    if output_encoding
      str.force_encoding(output_encoding)
    end
  end
  (new(str, **options) << row).string
end

.generate_lines(rows, **options) ⇒ Object

:call-seq:

CSV.generate_lines(rows)
CSV.generate_lines(rows, **options)

Returns the String created by generating CSV from using the specified options.

Argument rows must be an Array of row. Row is Array of String or CSV::Row.

Special options:

  • Option :row_sep defaults to "\n" on Ruby 3.0 or later and $INPUT_RECORD_SEPARATOR ($/) otherwise.:

    $INPUT_RECORD_SEPARATOR # => "\n"
    
  • This method accepts an additional option, :encoding, which sets the base Encoding for the output. This method will try to guess your Encoding from the first non-nil field in row, if possible, but you may need to use this parameter as a backup plan.

For other options, see Options for Generating.


Returns the String generated from an

CSV.generate_lines([['foo', '0'], ['bar', '1'], ['baz', '2']]) # => "foo,0\nbar,1\nbaz,2\n"

Raises an exception

# Raises NoMethodError (undefined method `each' for :foo:Symbol)
CSV.generate_lines(:foo)


1558
1559
1560
1561
1562
1563
1564
# File 'lib/csv.rb', line 1558

def generate_lines(rows, **options)
  self.generate(**options) do |csv|
    rows.each do |row|
      csv << row
    end
  end
end

.instance(data = $stdout, **options) ⇒ Object

:call-seq:

instance(string, **options)
instance(io = $stdout, **options)
instance(string, **options) {|csv| ... }
instance(io = $stdout, **options) {|csv| ... }

Creates or retrieves cached CSV objects. For arguments and options, see CSV.new.

This API is not Ractor-safe.


With no block given, returns a CSV object.

The first call to instance creates and caches a CSV object:

s0 = 's0'
csv0 = CSV.instance(s0)
csv0.class # => CSV

Subsequent calls to instance with that same string or io retrieve that same cached object:

csv1 = CSV.instance(s0)
csv1.class # => CSV
csv1.equal?(csv0) # => true # Same CSV object

A subsequent call to instance with a different string or io creates and caches a different CSV object.

s1 = 's1'
csv2 = CSV.instance(s1)
csv2.equal?(csv0) # => false # Different CSV object

All the cached objects remains available:

csv3 = CSV.instance(s0)
csv3.equal?(csv0) # true # Same CSV object
csv4 = CSV.instance(s1)
csv4.equal?(csv2) # true # Same CSV object

When a block is given, calls the block with the created or retrieved CSV object; returns the block’s return value:

CSV.instance(s0) {|csv| :foo } # => :foo


1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
# File 'lib/csv.rb', line 1026

def instance(data = $stdout, **options)
  # create a _signature_ for this method call, data object and options
  sig = [data.object_id] +
        options.values_at(*DEFAULT_OPTIONS.keys)

  # fetch or create the instance for this signature
  @@instances ||= Hash.new
  instance = (@@instances[sig] ||= new(data, **options))

  if block_given?
    yield instance  # run block, if given, returning result
  else
    instance        # or return the instance
  end
end

.open(filename_or_io, mode = "r", **options) ⇒ Object

:call-seq:

open(path_or_io, mode = "rb", **options ) -> new_csv
open(path_or_io, mode = "rb", **options ) { |csv| ... } -> object

possible options elements:

keyword form:
  :invalid => nil      # raise error on invalid byte sequence (default)
  :invalid => :replace # replace invalid byte sequence
  :undef => :replace   # replace undefined conversion
  :replace => string   # replacement string ("?" or "\uFFFD" if not specified)
  • Argument path_or_io, must be a file path or an IO stream.

:include: ../doc/csv/arguments/io.rdoc

  • Argument mode, if given, must be a File mode. See Access Modes.

  • Arguments **options must be keyword options. See Options for Generating.

  • This method optionally accepts an additional :encoding option that you can use to specify the Encoding of the data read from path or io. You must provide this unless your data is in the encoding given by Encoding::default_external. Parsing will use this to determine how to parse the data. You may provide a second Encoding to have the data transcoded as it is read. For example,

    encoding: 'UTF-32BE:UTF-8'
    

    would read UTF-32BE data from the file but transcode it to UTF-8 before parsing.


These examples assume prior execution of:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)

string_io = StringIO.new
string_io << "foo,0\nbar,1\nbaz,2\n"

With no block given, returns a new CSV object.

Create a CSV object using a file path:

csv = CSV.open(path)
csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Create a CSV object using an open File:

csv = CSV.open(File.open(path))
csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Create a CSV object using a StringIO:

csv = CSV.open(string_io)
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

With a block given, calls the block with the created CSV object; returns the block’s return value:

Using a file path:

csv = CSV.open(path) {|csv| p csv}
csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Output:

#<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Using an open File:

csv = CSV.open(File.open(path)) {|csv| p csv}
csv # => #<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Output:

#<CSV io_type:File io_path:"t.csv" encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Using a StringIO:

csv = CSV.open(string_io) {|csv| p csv}
csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Output:

#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"">

Raises an exception if the argument is not a String object or IO object:

# Raises TypeError (no implicit conversion of Symbol into String)
CSV.open(:foo)


1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
# File 'lib/csv.rb', line 1647

def open(filename_or_io, mode="r", **options)
  # wrap a File opened with the remaining +args+ with no newline
  # decorator
  file_opts = {}
  may_enable_bom_detection_automatically(filename_or_io,
                                         mode,
                                         options,
                                         file_opts)
  file_opts.merge!(options)
  unless file_opts.key?(:newline)
    file_opts[:universal_newline] ||= false
  end
  options.delete(:invalid)
  options.delete(:undef)
  options.delete(:replace)
  options.delete_if {|k, _| /newline\z/.match?(k)}

  if filename_or_io.is_a?(StringIO)
    f = create_stringio(filename_or_io.string, mode, **file_opts)
  else
    begin
      f = File.open(filename_or_io, mode, **file_opts)
    rescue ArgumentError => e
      raise unless /needs binmode/.match?(e.message) and mode == "r"
      mode = "rb"
      file_opts = {encoding: Encoding.default_external}.merge(file_opts)
      retry
    end
  end

  begin
    csv = new(f, **options)
  rescue Exception
    f.close
    raise
  end

  # handle blocks like Ruby's open(), not like the CSV library
  if block_given?
    begin
      yield csv
    ensure
      csv.close
    end
  else
    csv
  end
end

.parse(str, **options, &block) ⇒ Object

:call-seq:

parse(string) -> array_of_arrays
parse(io) -> array_of_arrays
parse(string, headers: ..., **options) -> csv_table
parse(io, headers: ..., **options) -> csv_table
parse(string, **options) {|row| ... }
parse(io, **options) {|row| ... }

Parses string or io using the specified options.

  • Argument string should be a String object; it will be put into a new StringIO object positioned at the beginning.

:include: ../doc/csv/arguments/io.rdoc

Without Option headers

Without headers case.

These examples assume prior execution of:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)

With no block given, returns an Array of Arrays formed from the source.

Parse a String:

a_of_a = CSV.parse(string)
a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

Parse an open File:

a_of_a = File.open(path) do |file|
  CSV.parse(file)
end
a_of_a # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

With a block given, calls the block with each parsed row:

Parse a String:

CSV.parse(string) {|row| p row }

Output:

["foo", "0"]
["bar", "1"]
["baz", "2"]

Parse an open File:

File.open(path) do |file|
  CSV.parse(file) {|row| p row }
end

Output:

["foo", "0"]
["bar", "1"]
["baz", "2"]
With Option headers

With headers case.

These examples assume prior execution of:

string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)

With no block given, returns a CSV::Table object formed from the source.

Parse a String:

csv_table = CSV.parse(string, headers: ['Name', 'Count'])
csv_table # => #<CSV::Table mode:col_or_row row_count:5>

Parse an open File:

csv_table = File.open(path) do |file|
  CSV.parse(file, headers: ['Name', 'Count'])
end
csv_table # => #<CSV::Table mode:col_or_row row_count:4>

With a block given, calls the block with each parsed row, which has been formed into a CSV::Row object:

Parse a String:

CSV.parse(string, headers: ['Name', 'Count']) {|row| p row }

Output:

# <CSV::Row "Name":"foo" "Count":"0">
# <CSV::Row "Name":"bar" "Count":"1">
# <CSV::Row "Name":"baz" "Count":"2">

Parse an open File:

File.open(path) do |file|
  CSV.parse(file, headers: ['Name', 'Count']) {|row| p row }
end

Output:

# <CSV::Row "Name":"foo" "Count":"0">
# <CSV::Row "Name":"bar" "Count":"1">
# <CSV::Row "Name":"baz" "Count":"2">

Raises an exception if the argument is not a String object or IO object:

# Raises NoMethodError (undefined method `close' for :foo:Symbol)
CSV.parse(:foo)

Please make sure if your text contains BOM or not. CSV.parse will not remove BOM automatically. You might want to remove BOM before calling CSV.parse :

# remove BOM on calling File.open
File.open(path, encoding: 'bom|utf-8') do |file|
  CSV.parse(file, headers: true) do |row|
    # you can get value by column name because BOM is removed
    p row['Name']
  end
end

Output:

# "foo"
# "bar"
# "baz"


1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
# File 'lib/csv.rb', line 1825

def parse(str, **options, &block)
  csv = new(str, **options)

  return csv.each(&block) if block_given?

  # slurp contents, if no block is given
  begin
    csv.read
  ensure
    csv.close
  end
end

.parse_line(line, **options) ⇒ Object

:call-seq:

CSV.parse_line(string) -> new_array or nil
CSV.parse_line(io) -> new_array or nil
CSV.parse_line(string, **options) -> new_array or nil
CSV.parse_line(io, **options) -> new_array or nil
CSV.parse_line(string, headers: true, **options) -> csv_row or nil
CSV.parse_line(io, headers: true, **options) -> csv_row or nil

Returns the data created by parsing the first line of string or io using the specified options.

  • Argument string should be a String object; it will be put into a new StringIO object positioned at the beginning.

:include: ../doc/csv/arguments/io.rdoc

Without Option headers

Without option headers, returns the first row as a new Array.

These examples assume prior execution of:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)

Parse the first line from a String object:

CSV.parse_line(string) # => ["foo", "0"]

Parse the first line from a File object:

File.open(path) do |file|
  CSV.parse_line(file) # => ["foo", "0"]
end # => ["foo", "0"]

Returns nil if the argument is an empty String:

CSV.parse_line('') # => nil
With Option headers

With headers, returns the first row as a CSV::Row object.

These examples assume prior execution of:

string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)

Parse the first line from a String object:

CSV.parse_line(string, headers: true) # => #<CSV::Row "Name":"foo" "Count":"0">

Parse the first line from a File object:

File.open(path) do |file|
  CSV.parse_line(file, headers: true)
end # => #<CSV::Row "Name":"foo" "Count":"0">

Raises an exception if the argument is nil:

# Raises ArgumentError (Cannot parse nil as CSV):
CSV.parse_line(nil)


1898
1899
1900
# File 'lib/csv.rb', line 1898

def parse_line(line, **options)
  new(line, **options).each.first
end

.read(path, **options) ⇒ Object

:call-seq:

read(source, **options) -> array_of_arrays
read(source, headers: true, **options) -> csv_table

Opens the given source with the given options (see CSV.open), reads the source (see CSV#read), and returns the result, which will be either an Array of Arrays or a CSV::Table.

Without headers:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
CSV.read(path) # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

With headers:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
CSV.read(path, headers: true) # => #<CSV::Table mode:col_or_row row_count:4>


1922
1923
1924
# File 'lib/csv.rb', line 1922

def read(path, **options)
  open(path, **options) { |csv| csv.read }
end

.readlines(path, **options) ⇒ Object

:call-seq:

CSV.readlines(source, **options)

Alias for CSV.read.



1930
1931
1932
# File 'lib/csv.rb', line 1930

def readlines(path, **options)
  read(path, **options)
end

.table(path, **options) ⇒ Object

:call-seq:

CSV.table(source, **options)

Calls CSV.read with source, options, and certain default options:

  • headers: true

  • converters: :numeric

  • header_converters: :symbol

Returns a CSV::Table object.

Example:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:4>


1949
1950
1951
1952
1953
1954
1955
1956
1957
# File 'lib/csv.rb', line 1949

def table(path, **options)
  default_options = {
    headers:           true,
    converters:        :numeric,
    header_converters: :symbol,
  }
  options = default_options.merge(options)
  read(path, **options)
end

Instance Method Details

#<<(row) ⇒ Object Also known as: add_row, puts

:call-seq:

csv << row -> self

Appends a row to self.

  • Argument row must be an Array object or a CSV::Row object.

  • The output stream must be open for writing.


Append Arrays:

CSV.generate do |csv|
  csv << ['foo', 0]
  csv << ['bar', 1]
  csv << ['baz', 2]
end # => "foo,0\nbar,1\nbaz,2\n"

Append CSV::Rows:

headers = []
CSV.generate do |csv|
  csv << CSV::Row.new(headers, ['foo', 0])
  csv << CSV::Row.new(headers, ['bar', 1])
  csv << CSV::Row.new(headers, ['baz', 2])
end # => "foo,0\nbar,1\nbaz,2\n"

Headers in CSV::Row objects are not appended:

headers = ['Name', 'Count']
CSV.generate do |csv|
  csv << CSV::Row.new(headers, ['foo', 0])
  csv << CSV::Row.new(headers, ['bar', 1])
  csv << CSV::Row.new(headers, ['baz', 2])
end # => "foo,0\nbar,1\nbaz,2\n"

Raises an exception if row is not an Array or CSV::Row:

CSV.generate do |csv|
  # Raises NoMethodError (undefined method `collect' for :foo:Symbol)
  csv << :foo
end

Raises an exception if the output stream is not opened for writing:

path = 't.csv'
File.write(path, '')
File.open(path) do |file|
  CSV.open(file) do |csv|
    # Raises IOError (not opened for writing)
    csv << ['foo', 0]
  end
end


2507
2508
2509
2510
# File 'lib/csv.rb', line 2507

def <<(row)
  writer << row
  self
end

#binmode?Boolean

Returns:

  • (Boolean)


2396
2397
2398
2399
2400
2401
2402
# File 'lib/csv.rb', line 2396

def binmode?
  if @io.respond_to?(:binmode?)
    @io.binmode?
  else
    false
  end
end

#col_sepObject

:call-seq:

csv.col_sep -> string

Returns the encoded column separator; used for parsing and writing; see col_sep:

CSV.new('').col_sep # => ","


2144
2145
2146
# File 'lib/csv.rb', line 2144

def col_sep
  parser.column_separator
end

#convert(name = nil, &converter) ⇒ Object

:call-seq:

convert(converter_name) -> array_of_procs
convert {|field, field_info| ... } -> array_of_procs
  • With no block, installs a field converter (a Proc).

  • With a block, defines and installs a custom field converter.

  • Returns the Array of installed field converters.

  • Argument converter_name, if given, should be the name of an existing field converter.

See Field Converters.


With no block, installs a field converter:

csv = CSV.new('')
csv.convert(:integer)
csv.convert(:float)
csv.convert(:date)
csv.converters # => [:integer, :float, :date]

The block, if given, is called for each field:

  • Argument field is the field value.

  • Argument field_info is a CSV::FieldInfo object containing details about the field.

The examples here assume the prior execution of:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)

Example giving a block:

csv = CSV.open(path)
csv.convert {|field, field_info| p [field, field_info]; field.upcase }
csv.read # => [["FOO", "0"], ["BAR", "1"], ["BAZ", "2"]]

Output:

["foo", #<struct CSV::FieldInfo index=0, line=1, header=nil>]
["0", #<struct CSV::FieldInfo index=1, line=1, header=nil>]
["bar", #<struct CSV::FieldInfo index=0, line=2, header=nil>]
["1", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
["baz", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
["2", #<struct CSV::FieldInfo index=1, line=3, header=nil>]

The block need not return a String object:

csv = CSV.open(path)
csv.convert {|field, field_info| field.to_sym }
csv.read # => [[:foo, :"0"], [:bar, :"1"], [:baz, :"2"]]

If converter_name is given, the block is not called:

csv = CSV.open(path)
csv.convert(:integer) {|field, field_info| fail 'Cannot happen' }
csv.read # => [["foo", 0], ["bar", 1], ["baz", 2]]

Raises a parse-time exception if converter_name is not the name of a built-in field converter:

csv = CSV.open(path)
csv.convert(:nosuch) => [nil]
# Raises NoMethodError (undefined method `arity' for nil:NilClass)
csv.read


2578
2579
2580
# File 'lib/csv.rb', line 2578

def convert(name = nil, &converter)
  parser_fields_converter.add_converter(name, &converter)
end

#convertersObject

:call-seq:

csv.converters -> array

Returns an Array containing field converters; see Field Converters:

csv = CSV.new('')
csv.converters # => []
csv.convert(:integer)
csv.converters # => [:integer]
csv.convert(proc {|x| x.to_s })
csv.converters

Notes that you need to call Ractor.make_shareable(CSV::Converters) on the main Ractor to use this method.



2217
2218
2219
2220
2221
2222
# File 'lib/csv.rb', line 2217

def converters
  parser_fields_converter.map do |converter|
    name = Converters.rassoc(converter)
    name ? name.first : converter
  end
end

#each(&block) ⇒ Object

:call-seq:

csv.each -> enumerator
csv.each {|row| ...}

Calls the block with each successive row. The data source must be opened for reading.

Without headers:

string = "foo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string)
csv.each do |row|
  p row
end

Output:

["foo", "0"]
["bar", "1"]
["baz", "2"]

With headers:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string, headers: true)
csv.each do |row|
  p row
end

Output:

<CSV::Row "Name":"foo" "Value":"0">
<CSV::Row "Name":"bar" "Value":"1">
<CSV::Row "Name":"baz" "Value":"2">

Raises an exception if the source is not opened for reading:

string = "foo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string)
csv.close
# Raises IOError (not opened for reading)
csv.each do |row|
  p row
end


2689
2690
2691
2692
2693
2694
2695
2696
2697
# File 'lib/csv.rb', line 2689

def each(&block)
  return to_enum(__method__) unless block_given?
  begin
    while true
      yield(parser_enumerator.next)
    end
  rescue StopIteration
  end
end

#eof?Boolean Also known as: eof

Returns:

  • (Boolean)


2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
# File 'lib/csv.rb', line 2432

def eof?
  return false if @eof_error
  begin
    parser_enumerator.peek
    false
  rescue MalformedCSVError => error
    @eof_error = error
    false
  rescue StopIteration
    true
  end
end

#field_size_limitObject

:call-seq:

csv.field_size_limit -> integer or nil

Returns the limit for field size; used for parsing; see field_size_limit:

CSV.new('').field_size_limit # => nil

Deprecated since 3.2.3. Use max_field_size instead.



2176
2177
2178
# File 'lib/csv.rb', line 2176

def field_size_limit
  parser.field_size_limit
end

#flock(*args) ⇒ Object

Raises:

  • (NotImplementedError)


2404
2405
2406
2407
# File 'lib/csv.rb', line 2404

def flock(*args)
  raise NotImplementedError unless @io.respond_to?(:flock)
  @io.flock(*args)
end

#force_quotes?Boolean

:call-seq:

csv.force_quotes? -> true or false

Returns the value that determines whether all output fields are to be quoted; used for generating; see force_quotes:

CSV.new('').force_quotes? # => false

Returns:

  • (Boolean)


2307
2308
2309
# File 'lib/csv.rb', line 2307

def force_quotes?
  @writer_options[:force_quotes]
end

#header_convert(name = nil, &converter) ⇒ Object

The block need not return a String object:

csv = CSV.open(path, headers: true)
csv.header_convert {|header, field_info| header.to_sym }
table = csv.read
table.headers # => [:Name, :Value]

If converter_name is given, the block is not called:

csv = CSV.open(path, headers: true)
csv.header_convert(:downcase) {|header, field_info| fail 'Cannot happen' }
table = csv.read
table.headers # => ["name", "value"]

Raises a parse-time exception if converter_name is not the name of a built-in field converter:

csv = CSV.open(path, headers: true)
csv.header_convert(:nosuch)
# Raises NoMethodError (undefined method `arity' for nil:NilClass)
csv.read


2644
2645
2646
# File 'lib/csv.rb', line 2644

def header_convert(name = nil, &converter)
  header_fields_converter.add_converter(name, &converter)
end

#header_convertersObject

:call-seq:

csv.header_converters -> array

Returns an Array containing header converters; used for parsing; see Header Converters:

CSV.new('').header_converters # => []

Notes that you need to call Ractor.make_shareable(CSV::HeaderConverters) on the main Ractor to use this method.



2283
2284
2285
2286
2287
2288
# File 'lib/csv.rb', line 2283

def header_converters
  header_fields_converter.map do |converter|
    name = HeaderConverters.rassoc(converter)
    name ? name.first : converter
  end
end

#header_row?Boolean

:call-seq:

csv.header_row? -> true or false

Returns true if the next row to be read is a header row; false otherwise.

Without headers:

string = "foo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string)
csv.header_row? # => false

With headers:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string, headers: true)
csv.header_row? # => true
csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
csv.header_row? # => false

Raises an exception if the source is not opened for reading:

string = "foo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string)
csv.close
# Raises IOError (not opened for reading)
csv.header_row?

Returns:

  • (Boolean)


2766
2767
2768
# File 'lib/csv.rb', line 2766

def header_row?
  parser.header_row?
end

#headersObject

:call-seq:

csv.headers -> object

Returns the value that determines whether headers are used; used for parsing; see headers:

CSV.new('').headers # => nil


2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
# File 'lib/csv.rb', line 2241

def headers
  if @writer
    @writer.headers
  else
    parsed_headers = parser.headers
    return parsed_headers if parsed_headers
    raw_headers = @parser_options[:headers]
    raw_headers = nil if raw_headers == false
    raw_headers
  end
end

#inspectObject

:call-seq:

csv.inspect -> string

Returns a String showing certain properties of self:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string, headers: true)
s = csv.inspect
s # => "#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:\",\" row_sep:\"\\n\" quote_char:\"\\\"\" headers:true>"


2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
# File 'lib/csv.rb', line 2825

def inspect
  str = ["#<", self.class.to_s, " io_type:"]
  # show type of wrapped IO
  if    @io == $stdout then str << "$stdout"
  elsif @io == $stdin  then str << "$stdin"
  elsif @io == $stderr then str << "$stderr"
  else                      str << @io.class.to_s
  end
  # show IO.path(), if available
  if @io.respond_to?(:path) and (p = @io.path)
    str << " io_path:" << p.inspect
  end
  # show encoding
  str << " encoding:" << @encoding.name
  # show other attributes
  ["lineno", "col_sep", "row_sep", "quote_char"].each do |attr_name|
    if a = __send__(attr_name)
      str << " " << attr_name << ":" << a.inspect
    end
  end
  ["skip_blanks", "liberal_parsing"].each do |attr_name|
    if a = __send__("#{attr_name}?")
      str << " " << attr_name << ":" << a.inspect
    end
  end
  _headers = headers
  str << " headers:" << _headers.inspect if _headers
  str << ">"
  begin
    str.join('')
  rescue  # any encoding error
    str.map do |s|
      e = Encoding::Converter.asciicompat_encoding(s.encoding)
      e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
    end.join('')
  end
end

#ioctl(*args) ⇒ Object

Raises:

  • (NotImplementedError)


2409
2410
2411
2412
# File 'lib/csv.rb', line 2409

def ioctl(*args)
  raise NotImplementedError unless @io.respond_to?(:ioctl)
  @io.ioctl(*args)
end

#liberal_parsing?Boolean

:call-seq:

csv.liberal_parsing? -> true or false

Returns the value that determines whether illegal input is to be handled; used for parsing; see liberal_parsing:

CSV.new('').liberal_parsing? # => false

Returns:

  • (Boolean)


2317
2318
2319
# File 'lib/csv.rb', line 2317

def liberal_parsing?
  parser.liberal_parsing?
end

#lineObject

:call-seq:

csv.line -> array

Returns the line most recently read:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
CSV.open(path) do |csv|
  csv.each do |row|
    p [csv.lineno, csv.line]
  end
end

Output:

[1, "foo,0\n"]
[2, "bar,1\n"]
[3, "baz,2\n"]


2382
2383
2384
# File 'lib/csv.rb', line 2382

def line
  parser.line
end

#linenoObject

:call-seq:

csv.line_no -> integer

Returns the count of the rows parsed or generated.

Parsing:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
CSV.open(path) do |csv|
  csv.each do |row|
    p [csv.lineno, row]
  end
end

Output:

[1, ["foo", "0"]]
[2, ["bar", "1"]]
[3, ["baz", "2"]]

Generating:

CSV.generate do |csv|
  p csv.lineno; csv << ['foo', 0]
  p csv.lineno; csv << ['bar', 1]
  p csv.lineno; csv << ['baz', 2]
end

Output:

0
1
2


2358
2359
2360
2361
2362
2363
2364
# File 'lib/csv.rb', line 2358

def lineno
  if @writer
    @writer.lineno
  else
    parser.lineno
  end
end

#max_field_sizeObject

:call-seq:

csv.max_field_size -> integer or nil

Returns the limit for field size; used for parsing; see max_field_size:

CSV.new('').max_field_size # => nil

Since 3.2.3.



2188
2189
2190
# File 'lib/csv.rb', line 2188

def max_field_size
  parser.max_field_size
end

#pathObject



2414
2415
2416
# File 'lib/csv.rb', line 2414

def path
  @io.path if @io.respond_to?(:path)
end

#quote_charObject

:call-seq:

csv.quote_char -> character

Returns the encoded quote character; used for parsing and writing; see quote_char:

CSV.new('').quote_char # => "\""


2164
2165
2166
# File 'lib/csv.rb', line 2164

def quote_char
  parser.quote_character
end

#readObject Also known as: readlines

:call-seq:

csv.read -> array or csv_table

Forms the remaining rows from self into:

  • A CSV::Table object, if headers are in use.

  • An Array of Arrays, otherwise.

The data source must be opened for reading.

Without headers:

string = "foo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
csv = CSV.open(path)
csv.read # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]

With headers:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
path = 't.csv'
File.write(path, string)
csv = CSV.open(path, headers: true)
csv.read # => #<CSV::Table mode:col_or_row row_count:4>

Raises an exception if the source is not opened for reading:

string = "foo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string)
csv.close
# Raises IOError (not opened for reading)
csv.read


2730
2731
2732
2733
2734
2735
2736
2737
# File 'lib/csv.rb', line 2730

def read
  rows = to_a
  if parser.use_headers?
    Table.new(rows, headers: parser.headers)
  else
    rows
  end
end

#return_headers?Boolean

:call-seq:

csv.return_headers? -> true or false

Returns the value that determines whether headers are to be returned; used for parsing; see return_headers:

CSV.new('').return_headers? # => false

Returns:

  • (Boolean)


2259
2260
2261
# File 'lib/csv.rb', line 2259

def return_headers?
  parser.return_headers?
end

#rewindObject

Rewinds the underlying IO object and resets CSV’s lineno() counter.



2447
2448
2449
2450
2451
2452
2453
# File 'lib/csv.rb', line 2447

def rewind
  @parser = nil
  @parser_enumerator = nil
  @eof_error = nil
  @writer.rewind if @writer
  @io.rewind
end

#row_sepObject

:call-seq:

csv.row_sep -> string

Returns the encoded row separator; used for parsing and writing; see row_sep:

CSV.new('').row_sep # => "\n"


2154
2155
2156
# File 'lib/csv.rb', line 2154

def row_sep
  parser.row_separator
end

#shiftObject Also known as: gets, readline

:call-seq:

csv.shift -> array, csv_row, or nil

Returns the next row of data as:

  • An Array if no headers are used.

  • A CSV::Row object if headers are used.

The data source must be opened for reading.

Without headers:

string = "foo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string)
csv.shift # => ["foo", "0"]
csv.shift # => ["bar", "1"]
csv.shift # => ["baz", "2"]
csv.shift # => nil

With headers:

string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string, headers: true)
csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
csv.shift # => #<CSV::Row "Name":"bar" "Value":"1">
csv.shift # => #<CSV::Row "Name":"baz" "Value":"2">
csv.shift # => nil

Raises an exception if the source is not opened for reading:

string = "foo,0\nbar,1\nbaz,2\n"
csv = CSV.new(string)
csv.close
# Raises IOError (not opened for reading)
csv.shift


2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
# File 'lib/csv.rb', line 2803

def shift
  if @eof_error
    eof_error, @eof_error = @eof_error, nil
    raise eof_error
  end
  begin
    parser_enumerator.next
  rescue StopIteration
    nil
  end
end

#skip_blanks?Boolean

:call-seq:

csv.skip_blanks? -> true or false

Returns the value that determines whether blank lines are to be ignored; used for parsing; see skip_blanks:

CSV.new('').skip_blanks? # => false

Returns:

  • (Boolean)


2296
2297
2298
# File 'lib/csv.rb', line 2296

def skip_blanks?
  parser.skip_blanks?
end

#skip_linesObject

:call-seq:

csv.skip_lines -> regexp or nil

Returns the Regexp used to identify comment lines; used for parsing; see skip_lines:

CSV.new('').skip_lines # => nil


2198
2199
2200
# File 'lib/csv.rb', line 2198

def skip_lines
  parser.skip_lines
end

#stat(*args) ⇒ Object

Raises:

  • (NotImplementedError)


2418
2419
2420
2421
# File 'lib/csv.rb', line 2418

def stat(*args)
  raise NotImplementedError unless @io.respond_to?(:stat)
  @io.stat(*args)
end

#to_iObject

Raises:

  • (NotImplementedError)


2423
2424
2425
2426
# File 'lib/csv.rb', line 2423

def to_i
  raise NotImplementedError unless @io.respond_to?(:to_i)
  @io.to_i
end

#to_ioObject



2428
2429
2430
# File 'lib/csv.rb', line 2428

def to_io
  @io.respond_to?(:to_io) ? @io.to_io : @io
end

#unconverted_fields?Boolean

:call-seq:

csv.unconverted_fields? -> object

Returns the value that determines whether unconverted fields are to be available; used for parsing; see unconverted_fields:

CSV.new('').unconverted_fields? # => nil

Returns:

  • (Boolean)


2231
2232
2233
# File 'lib/csv.rb', line 2231

def unconverted_fields?
  parser.unconverted_fields?
end

#write_headers?Boolean

:call-seq:

csv.write_headers? -> true or false

Returns the value that determines whether headers are to be written; used for generating; see write_headers:

CSV.new('').write_headers? # => nil

Returns:

  • (Boolean)


2269
2270
2271
# File 'lib/csv.rb', line 2269

def write_headers?
  @writer_options[:write_headers]
end