Ruby CSV Library

repository·main·Indexed 42 days ago

https://github.com/ruby/csv

A complete interface for reading and writing CSV (Comma-Separated Values) data in Ruby. It supports working with Strings and IO objects, providing methods like CSV.foreach for memory-efficient iteration, CSV.parse for string conversion, and CSV.generate for creating CSV data. The library includes support for headers, field and header converters (such as :integer, :float, and :symbol), and custom options for separators and quoting.

Tokens
11.4K
Snippets
47
Records
60
Agent score
53%

What's inside CSV

  1. Install the CSV gem

    main

    To use the CSV library in your Ruby application, add it to your Gemfile and run bundle.

    Alternatively, you can install the gem directly from your terminal using gem install csv.

    gem 'csv'
  2. How CSV handles character encoding

    main

    A CSV::Parser is multi-language (m17n) aware. The parser operates within the Encoding of the IO or String object being read.

    Key behaviors:

    • Data is never transcoded automatically by the parser; it is parsed in its original encoding.
    • The parser itself is transcoded into the input's encoding to ensure compatibility.
    • Consequently, CSV returns Arrays or Rows containing Strings in the same encoding as the source data.
  3. Use CSV with Headers

    main

    When parsing CSV data with headers, you can either specify headers: true (to use the first row of the data as headers) or provide an array of header names via the headers option. When headers are used, parsing methods return a CSV::Table object containing CSV::Row objects instead of plain Arrays.

    # Headers are part of the data
    data = CSV.parse(<<~ROWS, headers: true)
      Name,Department,Salary
      Bob,Engineering,1000
      Jane,Sales,2000
    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 manually
    data = CSV.parse('Bob,Engineering,1000', headers: %i[name department salary])
  4. Use Header Converters to transform column names

    main

    Header converters transform the header names during parsing. Use the header_converters option with a Proc, a symbol, or a list. These are stored in CSV::HeaderConverters.

    # Using built-in :downcase
    CSV.parse(string, headers: true, header_converters: :downcase)
    
    # Using built-in :symbol (strips whitespace, downcases, replaces spaces with underscores)
    CSV.parse(string, headers: true, header_converters: :symbol)
    
    # Using a custom Proc
    upcase_converter = proc {|header| header.upcase }
    CSV.parse(string, headers: true, header_converters: upcase_converter)
  5. Use Field Converters to transform parsed values

    main

    Field converters intercept parsed values and transform them before they are returned. You can pass a Proc, a symbol representing a built-in converter, or an array of both to the converters option.

    # Using a built-in converter
    CSV.parse(string, converters: :integer)
    
    # Using a custom Proc converter
    strip_converter = proc {|field| field.strip }
    CSV.parse(string, converters: strip_converter)
    
    # Using a converter list
    CSV.parse(string, converters: [:integer, :float])
    
    # Registering and using a custom named converter
    CSV::Converters[:strip] = proc {|field| field.strip }
    CSV.parse(string, converters: :strip)
  6. How CSV::Table access modes work

    main

    A CSV::Table instance can operate in three different modes, which change how indexing and iteration behave. The mode determines whether [], []=, delete, delete_if, each, and values_at act on rows or columns.

    1. Row Mode (:row): Accessing by an Integer index returns a CSV::Row. Accessing by a Range returns an array of CSV::Row objects.
    2. Column Mode (:col): Accessing by an Integer index returns an Array of column data. Accessing by a String header returns an Array of column data.
    3. Mixed Mode (:col_or_row): This is the default mode. It allows dual access: Integer or Range indices refer to rows, while String indices refer to columns.

    You can switch modes in-place using bang methods (e.g., by_row!) or create a duplicate in a specific mode using non-bang methods (e.g., by_row).

    source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
    table = CSV.parse(source, headers: true)
    
    # Switch to row mode in-place
    table.by_row!
    table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
    
    # Switch to column mode in-place
    table.by_col!
    table['Name'] # => ["foo", "bar", "baz"]
    
    # Switch to mixed mode (default)
    table.by_col_or_row!
    table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
    table['Name'] # => ["foo", "bar", "baz"]
  7. Handle encoding in CSV parsing

    main
    The CSV::Parser is multi-lingual (m17n) aware. It operates using the encoding of the IO or String object being read. Data is parsed in its original encoding without automatic transcoding unless explicitly requested via Ruby's transcoding mechanisms. Consequently, the resulting Arrays or Rows of Strings will maintain the encoding of the source data.
  8. Use Write Converters for CSV generation

    main

    Write converters allow you to modify values just before they are written to the CSV output. Use the write_converters option in CSV.generate or when creating a new CSV instance.

    strip_converter = proc {|field| field.respond_to?(:strip) ? field.strip : field }
    upcase_converter = proc {|field| field.respond_to?(:upcase) ? field.upcase : field }
    
    output_string = CSV.generate(write_converters: [strip_converter, upcase_converter]) do |csv|
      csv << [' foo ', 0]
    end
    # => "FOO,0\n"
  9. Read CSV files line by line with CSV.foreach

    main

    The CSV.foreach method is the standard way to iterate over a CSV file. It reads the file one row at a time, which is memory-efficient for large files. The block yields a row object for each line in the file.

    require "csv"
    
    CSV.foreach("path/to/file.csv") do |row|
      # use row here...
    end
  10. Configure column and row separators

    main

    You can specify the characters used to delimit columns and rows:

    • column_separator (or col_sep): The character(s) separating fields. Must be 1 or more characters.
    • row_separator (or row_sep): The character(s) separating rows. Setting this to :auto enables automatic detection of line endings (e.g., `

    , , or `).

  11. Configure skip_lines for filtering rows

    main

    The skip_lines option allows you to skip rows that match a specific pattern. It accepts:

    • A String: Rows containing this string will be skipped.
    • A Regexp: Rows matching this regular expression will be skipped.
    • An object that responds to #match.
  12. Configure liberal parsing options

    main

    The liberal_parsing option allows for more relaxed CSV parsing. It can be passed as a boolean or a Hash to control specific behaviors:

    • liberal_parsing: true: Enables general liberal parsing.
    • liberal_parsing: { double_quote_outside_quote: true }: Allows double quotes to appear outside of quoted fields (they will be treated as part of the data).
    • liberal_parsing: { backslash_quote: true }: Enables backslash escaping for quotes.