require 'csv' require 'fastcsv/fastcsv' # @see https://github.com/ruby/ruby/blob/ab337e61ecb5f42384ba7d710c36faf96a454e5c/lib/csv.rb class FastCSV < CSV def self.raw_parse(*args, &block) Parser.new.raw_parse(*args, &block) end def row parser && parser.row end def shift # COPY # handle headers not based on document content if header_row? and @return_headers and [Array, String].include? @use_headers.class if @unconverted_fields return add_unconverted_fields(parse_headers, Array.new) else return parse_headers end end # PASTE # The CSV library wraps File objects, whereas `FastCSV.raw_parse` accepts # IO-like objects that implement `#read(length)`. begin unless csv = fiber.resume # was unless parse = @io.gets(@row_sep) return nil end rescue FiberError return nil end row = parser.row # COPY if csv.empty? # # I believe a blank line should be an Array.new, not Ruby 1.8 # CSV's [nil] # if row.empty? # was if parse.empty? @lineno += 1 if @skip_blanks return shift # was next elsif @unconverted_fields return add_unconverted_fields(Array.new, Array.new) elsif @use_headers return self.class::Row.new(Array.new, Array.new) else return Array.new end end end # PASTE return shift if @skip_lines and @skip_lines.match row # was next if @skip_lines and @skip_lines.match parse # COPY @lineno += 1 # save fields unconverted fields, if needed... unconverted = csv.dup if @unconverted_fields # convert fields, if needed... csv = convert_fields(csv) unless @use_headers or @converters.empty? # parse out header rows and handle CSV::Row conversions... csv = parse_headers(csv) if @use_headers # inject unconverted fields and accessor, if requested... if @unconverted_fields and not csv.respond_to? :unconverted_fields add_unconverted_fields(csv, unconverted) end # PASTE csv # was break csv end # CSV's delegated and overwritten IO methods move the pointer within the file, # but FastCSV doesn't notice, so we need to recreate the fiber. The old fiber # is garbage collected. def pos=(*args) super @parser = nil @fiber = nil end def reopen(*args) super @parser = nil @fiber = nil end def seek(*args) super @parser = nil @fiber = nil end def rewind super @parser = nil @fiber = nil end private def parser @parser ||= Parser.new end def fiber # @see http://www.ruby-doc.org/core-2.1.4/Fiber.html @fiber ||= Fiber.new do if @io.respond_to?(:internal_encoding) enc2 = @io.external_encoding enc = @io.internal_encoding || '-' if enc2 encoding = "#{enc2}:#{enc}" else encoding = enc end end parser.raw_parse(@io, encoding: encoding, quote_char: quote_char, col_sep: col_sep, row_sep: row_sep) do |row| Fiber.yield(row) end end end end def FastCSV(*args, &block) FastCSV.instance(*args, &block) end