1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/lib/csv/writer.rb
kou e5d634260e Import CSV 3.0.2
This includes performance improvement especially writing. Writing is
about 2 times faster.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66507 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-12-23 07:00:35 +00:00

144 lines
3.8 KiB
Ruby

# frozen_string_literal: true
require_relative "match_p"
require_relative "row"
using CSV::MatchP if CSV.const_defined?(:MatchP)
class CSV
class Writer
attr_reader :lineno
attr_reader :headers
def initialize(output, options)
@output = output
@options = options
@lineno = 0
prepare
if @options[:write_headers] and @headers
self << @headers
end
end
def <<(row)
case row
when Row
row = row.fields
when Hash
row = @headers.collect {|header| row[header]}
end
@headers ||= row if @use_headers
@lineno += 1
line = row.collect(&@quote).join(@column_separator) + @row_separator
if @output_encoding
line = line.encode(@output_encoding)
end
@output << line
self
end
def rewind
@lineno = 0
@headers = nil if @options[:headers].nil?
end
private
def prepare
@encoding = @options[:encoding]
prepare_header
prepare_format
prepare_output
end
def prepare_header
headers = @options[:headers]
case headers
when Array
@headers = headers
@use_headers = true
when String
@headers = CSV.parse_line(headers,
col_sep: @options[:column_separator],
row_sep: @options[:row_separator],
quote_char: @options[:quote_character])
@use_headers = true
when true
@headers = nil
@use_headers = true
else
@headers = nil
@use_headers = false
end
return unless @headers
converter = @options[:header_fields_converter]
@headers = converter.convert(@headers, nil, 0)
@headers.each do |header|
header.freeze if header.is_a?(String)
end
end
def prepare_format
@column_separator = @options[:column_separator].to_s.encode(@encoding)
row_separator = @options[:row_separator]
if row_separator == :auto
@row_separator = $INPUT_RECORD_SEPARATOR.encode(@encoding)
else
@row_separator = row_separator.to_s.encode(@encoding)
end
quote_character = @options[:quote_character]
quote = lambda do |field|
field = String(field)
encoded_quote_character = quote_character.encode(field.encoding)
encoded_quote_character +
field.gsub(encoded_quote_character,
encoded_quote_character * 2) +
encoded_quote_character
end
if @options[:force_quotes]
@quote = quote
else
quotable_pattern =
Regexp.new("[\r\n".encode(@encoding) +
Regexp.escape(@column_separator) +
Regexp.escape(quote_character.encode(@encoding)) +
"]".encode(@encoding))
@quote = lambda do |field|
if field.nil? # represent +nil+ fields as empty unquoted fields
""
else
field = String(field) # Stringify fields
# represent empty fields as empty quoted fields
if field.empty? or quotable_pattern.match?(field)
quote.call(field)
else
field # unquoted field
end
end
end
end
end
def prepare_output
@output_encoding = nil
return unless @output.is_a?(StringIO)
output_encoding = @output.internal_encoding || @output.external_encoding
if @encoding != output_encoding
if @options[:force_encoding]
@output_encoding = output_encoding
else
compatible_encoding = Encoding.compatible?(@encoding, output_encoding)
if compatible_encoding
@output.set_encoding(compatible_encoding)
@output.seek(0, IO::SEEK_END)
end
end
end
end
end
end