summaryrefslogtreecommitdiff
path: root/lib/csv.rb
diff options
context:
space:
mode:
authornahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-05-18 12:34:33 +0000
committernahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-05-18 12:34:33 +0000
commit34a553da2ee21e96999c150f80b7231d474a48d3 (patch)
treedd3b69b5279a390e046ff8b2f7eed13c2614298e /lib/csv.rb
parent75db654808404ad6f7ec79c3f9ae6fda8a3c827e (diff)
* lib/csv.rb: writes lines with "\n" when row separator is not given.
formerly it was "\r\n". * lib/csv.rb: [CAUTION] API change * CSV::Row removed. a row is represented as just an Array. since CSV::Row was a subclass of Array, it won't hurt almost all programs except one which depended CSV::Row#match. * CSV::Cell removed. a cell is represented as just a String or nil(NULL). this change will cause widespread destruction. CSV.open("foo.csv", "r") do |row| row.each do |cell| if cell.is_null # Cell#is_null p "(NULL)" else p cell.data # Cell#data end end end must be just; CSV.open("foo.csv", "r") do |row| row.each do |cell| if cell.nil? p "(NULL)" else p cell end end end * lib/csv.rb: [CAUTION] record separator(CR, LF, CR+LF) behavior change. CSV.open, CSV.parse, and CSV,generate now do not force opened file binmode. formerly it set binmode explicitly. with CSV.open, binmode of opened file depends the given mode parameter "r", "w", "rb", and "wb". CSV.parse and CSV.generate open file with "r" and "w". setting mode properly is user's responsibility now. * lib/csv.rb: accepts String as a fs (field separator/column separator) and rs (record separator/row separator) * lib/csv.rb: added CSV.foreach(path, rs = nil, &block). CSV.foreach now does not handle "| cmd" as a path different from IO.foreach. needed? * test/csv/test_csv.rb: updated. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@6359 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/csv.rb')
-rw-r--r--lib/csv.rb545
1 files changed, 244 insertions, 301 deletions
diff --git a/lib/csv.rb b/lib/csv.rb
index 3eb13192fe..351976fd00 100644
--- a/lib/csv.rb
+++ b/lib/csv.rb
@@ -1,106 +1,30 @@
# CSV -- module for generating/parsing CSV data.
-
+# Copyright (C) 2000-2004 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>.
+
# $Id$
-
+
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
# either the dual license version in 2003, or any later version.
-
-
+
+
class CSV
-
- # Describes a cell of CSV.
- class Cell
- # Datum as string.
- attr_accessor :data
-
- # Is this datum NULL?
- attr_accessor :is_null
-
- # If is_null is true, datum is stored in the instance created but it
- # should be treated as 'NULL'.
- def initialize(data = '', is_null = true)
- @data = data
- @is_null = is_null
- end
-
- # Compares another cell with self. Bear in mind NULL matches with NULL.
- # Use CSV::Cell#== if you don't want NULL matches with NULL.
- # rhs: an instance of CSV::Cell to be compared.
- def match(rhs)
- if @is_null and rhs.is_null
- true
- elsif @is_null or rhs.is_null
- false
- else
- @data == rhs.data
- end
- end
-
- # Compares another cell with self. Bear in mind NULL does not match with
- # NULL. Use CSV::Cell#match if you want NULL matches with NULL.
- # rhs: an instance of CSV::Cell to be compared.
- def ==(rhs)
- if @is_null or rhs.is_null
- false
- else
- @data == rhs.data
- end
- end
-
- def to_str
- content.to_str
- end
-
- def to_s
- content.to_s
- end
-
- private
-
- def content
- @is_null ? nil : data
- end
- end
-
-
- # Describes a row of CSV. Each element must be a CSV::Cell.
- class Row < Array
-
- # Returns the strings contained in the row's cells.
- def to_a
- self.collect { |cell| cell.is_null ? nil : cell.data }
- end
-
- # Compares another row with self.
- # rhs: an Array of cells. Each cell should be a CSV::Cell.
- def match(rhs)
- if self.size != rhs.size
- return false
- end
- for idx in 0...(self.size)
- unless self[idx].match(rhs[idx])
- return false
- end
- end
- true
- end
- end
-
-
class IllegalFormatError < RuntimeError; end
-
- def CSV.open(filename, mode, col_sep = ?,, row_sep = nil, &block)
+ def CSV.open(path, mode, fs = ',', rs = nil, &block)
if mode == 'r' or mode == 'rb'
- open_reader(filename, col_sep, row_sep, &block)
+ open_reader(path, mode, fs, rs, &block)
elsif mode == 'w' or mode == 'wb'
- open_writer(filename, col_sep, row_sep, &block)
+ open_writer(path, mode, fs, rs, &block)
else
raise ArgumentError.new("'mode' must be 'r', 'rb', 'w', or 'wb'")
end
end
+ def CSV.foreach(path, rs = nil, &block)
+ open_reader(path, 'r', ',', rs, &block)
+ end
+
# Open a CSV formatted file for reading.
#
# EXAMPLE 1
@@ -127,8 +51,8 @@ class CSV
# RETURNS
# reader instance. To get parse result, see CSV::Reader#each.
#
- def CSV.parse(filename, col_sep = ?,, row_sep = nil, &block)
- open_reader(filename, col_sep, row_sep, &block)
+ def CSV.parse(path, fs = ',', rs = nil, &block)
+ open_reader(path, 'r', fs, rs, &block)
end
# Open a CSV formatted file for writing.
@@ -156,8 +80,8 @@ class CSV
# writer instance. See CSV::Writer#<< and CSV::Writer#add_row to know how
# to generate CSV string.
#
- def CSV.generate(filename, col_sep = ?,, row_sep = nil, &block)
- open_writer(filename, col_sep, row_sep, &block)
+ def CSV.generate(path, fs = ',', rs = nil, &block)
+ open_writer(path, 'w', fs, rs, &block)
end
# Parse a line from given string. Bear in mind it parses ONE LINE. Rest of
@@ -166,47 +90,52 @@ class CSV
#
# If you don't know whether a target string to parse is exactly 1 line or
# not, use CSV.parse_row instead of this method.
- def CSV.parse_line(src, col_sep = ?,, row_sep = nil)
+ def CSV.parse_line(src, fs = ',', rs = nil)
+ if !fs.nil? and fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
idx = 0
res_type = :DT_COLSEP
- cells = Row.new
+ row = []
begin
while (res_type.equal?(:DT_COLSEP))
- cell = Cell.new
- res_type, idx = parse_body(src, idx, cell, col_sep, row_sep)
- cells.push(cell.is_null ? nil : cell.data)
+ res_type, idx, cell = parse_body(src, idx, fs, rs)
+ row << cell
end
rescue IllegalFormatError
- return Row.new
+ return []
end
- cells
+ row
end
# Create a line from cells. each cell is stringified by to_s.
- def CSV.generate_line(cells, col_sep = ?,, row_sep = nil)
- if (cells.size == 0)
+ def CSV.generate_line(row, fs = ',', rs = nil)
+ if (row.size == 0)
return ''
end
+ if !fs.nil? and fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
res_type = :DT_COLSEP
result_str = ''
idx = 0
while true
- cell = if (cells[idx].nil?)
- Cell.new('', true)
- else
- Cell.new(cells[idx].to_s, false)
- end
- generate_body(cell, result_str, col_sep, row_sep)
+ generate_body(row[idx], result_str, fs, rs)
idx += 1
- if (idx == cells.size)
+ if (idx == row.size)
break
end
- generate_separator(:DT_COLSEP, result_str, col_sep, row_sep)
+ generate_separator(:DT_COLSEP, result_str, fs, rs)
end
result_str
end
-
-
+
# Parse a line from string. Consider using CSV.parse_line instead.
# To parse lines in CSV string, see EXAMPLE below.
#
@@ -236,16 +165,21 @@ class CSV
# parsed_cells: num of parsed cells.
# idx: index of next parsing location of 'src'.
#
- def CSV.parse_row(src, idx, out_dev, col_sep = ?,, row_sep = nil)
+ def CSV.parse_row(src, idx, out_dev, fs = ',', rs = nil)
+ if !fs.nil? and fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
idx_backup = idx
parsed_cells = 0
res_type = :DT_COLSEP
begin
while (!res_type.equal?(:DT_ROWSEP))
- cell = Cell.new
- res_type, idx = parse_body(src, idx, cell, col_sep, row_sep)
+ res_type, idx, cell = parse_body(src, idx, fs, rs)
if res_type.equal?(:DT_EOS)
- if idx == idx_backup #((parsed_cells == 0) && (cell.is_null))
+ if idx == idx_backup #((parsed_cells == 0) and cell.nil?)
return 0, 0
end
res_type = :DT_ROWSEP
@@ -258,8 +192,7 @@ class CSV
end
return parsed_cells, idx
end
-
-
+
# Convert a line from cells data to string. Consider using CSV.generate_line
# instead. To generate multi-row CSV string, see EXAMPLE below.
#
@@ -292,39 +225,46 @@ class CSV
# RETURNS
# parsed_cells: num of converted cells.
#
- def CSV.generate_row(src, cells, out_dev, col_sep = ?,, row_sep = nil)
+ def CSV.generate_row(src, cells, out_dev, fs = ',', rs = nil)
+ if !fs.nil? and fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
src_size = src.size
if (src_size == 0)
if cells == 0
- generate_separator(:DT_ROWSEP, out_dev, col_sep, row_sep)
+ generate_separator(:DT_ROWSEP, out_dev, fs, rs)
end
return 0
end
res_type = :DT_COLSEP
parsed_cells = 0
- generate_body(src[parsed_cells], out_dev, col_sep, row_sep)
+ generate_body(src[parsed_cells], out_dev, fs, rs)
parsed_cells += 1
- while ((parsed_cells < cells) && (parsed_cells != src_size))
- generate_separator(:DT_COLSEP, out_dev, col_sep, row_sep)
- generate_body(src[parsed_cells], out_dev, col_sep, row_sep)
+ while ((parsed_cells < cells) and (parsed_cells != src_size))
+ generate_separator(:DT_COLSEP, out_dev, fs, rs)
+ generate_body(src[parsed_cells], out_dev, fs, rs)
parsed_cells += 1
end
if (parsed_cells == cells)
- generate_separator(:DT_ROWSEP, out_dev, col_sep, row_sep)
+ generate_separator(:DT_ROWSEP, out_dev, fs, rs)
else
- generate_separator(:DT_COLSEP, out_dev, col_sep, row_sep)
+ generate_separator(:DT_COLSEP, out_dev, fs, rs)
end
parsed_cells
end
-
+
+ # Private class methods.
class << self
private
- def open_reader(filename, col_sep, row_sep, &block)
- file = File.open(filename, 'rb')
+ def open_reader(path, mode, fs, rs, &block)
+ file = File.open(path, mode)
if block
begin
- CSV::Reader.parse(file, col_sep, row_sep) do |row|
+ CSV::Reader.parse(file, fs, rs) do |row|
yield(row)
end
ensure
@@ -332,17 +272,17 @@ class CSV
end
nil
else
- reader = CSV::Reader.create(file, col_sep, row_sep)
+ reader = CSV::Reader.create(file, fs, rs)
reader.close_on_terminate
reader
end
end
- def open_writer(filename, col_sep, row_sep, &block)
- file = File.open(filename, 'wb')
+ def open_writer(path, mode, fs, rs, &block)
+ file = File.open(path, mode)
if block
begin
- CSV::Writer.generate(file, col_sep, row_sep) do |writer|
+ CSV::Writer.generate(file, fs, rs) do |writer|
yield(writer)
end
ensure
@@ -350,147 +290,175 @@ class CSV
end
nil
else
- writer = CSV::Writer.create(file, col_sep, row_sep)
+ writer = CSV::Writer.create(file, fs, rs)
writer.close_on_terminate
writer
end
end
- def parse_body(src, idx, cell, col_sep, row_sep)
- row_sep_end = row_sep || ?\n
- cell.is_null = false
+ def parse_body(src, idx, fs, rs)
+ fs_str = fs
+ fs_size = fs_str.size
+ fs_idx = 0
+ rs_str = rs || "\n"
+ rs_size = rs_str.size
+ rs_idx = 0
+ cell = ''
state = :ST_START
quoted = false
cr = false
c = nil
+ last_idx = idx
while (c = src[idx])
- idx += 1
- result_state = :DT_UNKNOWN
- if (c == col_sep)
- if state.equal?(:ST_DATA)
- if cr
- raise IllegalFormatError.new
- end
- if (!quoted)
- state = :ST_END
- result_state = :DT_COLSEP
- else
- cell.data << c.chr
- end
- elsif state.equal?(:ST_QUOTE)
- if cr
- raise IllegalFormatError.new
- end
- state = :ST_END
- result_state = :DT_COLSEP
- else # :ST_START
- cell.is_null = true
- state = :ST_END
- result_state = :DT_COLSEP
+ if c == ?"
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ if cr
+ raise IllegalFormatError
+ end
+ if fs_idx != 0
+ fs_idx = 0
+ end
+ if rs_idx != 0
+ rs_idx = 0
end
- elsif (c == ?") # " for vim syntax hilighting.
if state.equal?(:ST_DATA)
- if cr
- raise IllegalFormatError.new
- end
if quoted
+ last_idx += 1
quoted = false
state = :ST_QUOTE
else
- raise IllegalFormatError.new
+ raise IllegalFormatError
end
elsif state.equal?(:ST_QUOTE)
- cell.data << c.chr
+ cell << c.chr
+ last_idx += 1
quoted = true
state = :ST_DATA
else # :ST_START
quoted = true
+ last_idx += 1
state = :ST_DATA
end
- elsif row_sep.nil? and c == ?\r
- if cr
- raise IllegalFormatError.new
+ elsif c == fs_str[fs_idx]
+ fs_idx += 1
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ if rs_idx != 0
+ rs_idx = 0
end
- if quoted
- cell.data << c.chr
- state = :ST_DATA
- else
- cr = true
- end
- elsif c == row_sep_end
- if state.equal?(:ST_DATA)
+ if fs_idx == fs_size
+ fs_idx = 0
if cr
- state = :ST_END
- result_state = :DT_ROWSEP
- cr = false
- else
+ raise IllegalFormatError
+ end
+ if state.equal?(:ST_DATA)
+ if rs_idx != 0
+ cell << rs_str[0, rs_idx]
+ rs_idx = 0
+ end
if quoted
- cell.data << c.chr
- state = :ST_DATA
+ true # ToDo: delete; dummy line for coverage
else
- state = :ST_END
- result_state = :DT_ROWSEP
+ return :DT_COLSEP, idx + 1, cell;
end
+ elsif state.equal?(:ST_QUOTE)
+ if rs_idx != 0
+ raise IllegalFormatError
+ end
+ return :DT_COLSEP, idx + 1, cell;
+ else # :ST_START
+ return :DT_COLSEP, idx + 1, nil
end
- elsif state.equal?(:ST_QUOTE)
- state = :ST_END
- result_state = :DT_ROWSEP
- if cr
- cr = false
+ end
+ elsif c == rs_str[rs_idx]
+ rs_idx += 1
+ unless (rs.nil? and cr)
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ end
+ if fs_idx != 0
+ fs_idx = 0
+ end
+ if rs_idx == rs_size
+ rs_idx = 0
+ if state.equal?(:ST_DATA)
+ if quoted
+ true # ToDo: delete; dummy line for coverage
+ else
+ return :DT_ROWSEP, idx + 1, cell
+ end
+ elsif state.equal?(:ST_QUOTE)
+ return :DT_ROWSEP, idx + 1, cell
+ else # :ST_START
+ return :DT_ROWSEP, idx + 1, nil
end
- else # :ST_START
- cell.is_null = true
- state = :ST_END
- result_state = :DT_ROWSEP
+ end
+ elsif rs.nil? and c == ?\r
+ # special \r treatment for backward compatibility
+ if cr
+ raise IllegalFormatError
+ end
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ if quoted
+ state = :ST_DATA
+ else
+ cr = true
end
else
- if state.equal?(:ST_DATA) || state.equal?(:ST_START)
+ if fs_idx != 0
+ fs_idx = 0
+ end
+ if rs_idx != 0
+ rs_idx = 0
+ end
+ if state.equal?(:ST_DATA) or state.equal?(:ST_START)
if cr
- raise IllegalFormatError.new
+ raise IllegalFormatError
end
- cell.data << c.chr
state = :ST_DATA
else # :ST_QUOTE
- raise IllegalFormatError.new
+ raise IllegalFormatError
end
end
- if state.equal?(:ST_END)
- return result_state, idx;
- end
+ idx += 1
end
if state.equal?(:ST_START)
- cell.is_null = true
- elsif state.equal?(:ST_QUOTE)
- true # dummy for coverate; only a data
+ return :DT_EOS, idx, nil
elsif quoted
- raise IllegalFormatError.new
+ raise IllegalFormatError
elsif cr
- raise IllegalFormatError.new
+ raise IllegalFormatError
end
- return :DT_EOS, idx
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ return :DT_EOS, idx, cell
end
-
- def generate_body(cells, out_dev, col_sep, row_sep)
- row_data = cells.data.dup
- if (!cells.is_null)
- if (row_data.gsub!('"', '""') ||
- row_data.include?(col_sep) ||
- (row_sep && row_data.index(row_sep)) ||
- (/[\r\n]/ =~ row_data) ||
- (cells.data.empty?))
+
+ def generate_body(cell, out_dev, fs, rs)
+ if cell.nil?
+ # empty
+ else
+ row_data = cell.dup
+ if (row_data.gsub!('"', '""') or
+ row_data.index(fs) or
+ (rs and row_data.index(rs)) or
+ (/[\r\n]/ =~ row_data) or
+ (cell.empty?))
out_dev << '"' << row_data << '"'
else
out_dev << row_data
end
end
end
-
- def generate_separator(type, out_dev, col_sep, row_sep)
+
+ def generate_separator(type, out_dev, fs, rs)
case type
when :DT_COLSEP
- out_dev << col_sep.chr
+ out_dev << fs
when :DT_ROWSEP
- out_dev << (row_sep ? row_sep.chr : "\r\n")
+ out_dev << (rs || "\n")
end
end
end
@@ -499,7 +467,7 @@ class CSV
# CSV formatted string/stream reader.
#
# EXAMPLE
- # read CSV lines until the first column is 'stop'.
+ # read CSV lines untill the first column is 'stop'.
#
# CSV::Reader.parse(File.open('bigdata', 'rb')) do |row|
# p row
@@ -511,8 +479,8 @@ class CSV
# Parse CSV data and get lines. Given block is called for each parsed row.
# Block value is always nil. Rows are not cached for performance reason.
- def Reader.parse(str_or_readable, col_sep = ?,, row_sep = nil)
- reader = create(str_or_readable, col_sep, row_sep)
+ def Reader.parse(str_or_readable, fs = ',', rs = nil)
+ reader = create(str_or_readable, fs, rs)
reader.each do |row|
yield(row)
end
@@ -521,20 +489,20 @@ class CSV
end
# Returns reader instance.
- def Reader.create(str_or_readable, col_sep = ?,, row_sep = nil)
+ def Reader.create(str_or_readable, fs = ',', rs = nil)
case str_or_readable
when IO
- IOReader.new(str_or_readable, col_sep, row_sep)
+ IOReader.new(str_or_readable, fs, rs)
when String
- StringReader.new(str_or_readable, col_sep, row_sep)
+ StringReader.new(str_or_readable, fs, rs)
else
- IOReader.new(str_or_readable, col_sep, row_sep)
+ IOReader.new(str_or_readable, fs, rs)
end
end
def each
while true
- row = Row.new
+ row = []
parsed_cells = get_row(row)
if parsed_cells == 0
break
@@ -545,7 +513,7 @@ class CSV
end
def shift
- row = Row.new
+ row = []
parsed_cells = get_row(row)
row
end
@@ -557,25 +525,23 @@ class CSV
private
def initialize(dev)
- raise RuntimeError.new('do not instantiate this class directly')
+ raise RuntimeError.new('Do not instanciate this class directly.')
end
def get_row(row)
- raise NotImplementedError.new(
- 'method get_row must be defined in a derived class')
+ raise NotImplementedError.new('Method get_row must be defined in a derived class.')
end
def terminate
# Define if needed.
end
end
-
+
class StringReader < Reader
-
- def initialize(string, col_sep = ?,, row_sep = nil)
- @col_sep = col_sep
- @row_sep = row_sep
+ def initialize(string, fs = ',', rs = nil)
+ @fs = fs
+ @rs = rs
@dev = string
@idx = 0
if @dev[0, 3] == "\xef\xbb\xbf"
@@ -586,9 +552,8 @@ class CSV
private
def get_row(row)
- parsed_cells, next_idx =
- CSV.parse_row(@dev, @idx, row, @col_sep, @row_sep)
- if parsed_cells == 0 && next_idx == 0 && @idx != @dev.size
+ parsed_cells, next_idx = CSV.parse_row(@dev, @idx, row, @fs, @rs)
+ if parsed_cells == 0 and next_idx == 0 and @idx != @dev.size
raise IllegalFormatError.new
end
@idx = next_idx
@@ -598,12 +563,10 @@ class CSV
class IOReader < Reader
-
- def initialize(io, col_sep = ?,, row_sep = nil)
+ def initialize(io, fs = ',', rs = nil)
@io = io
- @io.binmode if @io.respond_to?(:binmode)
- @col_sep = col_sep
- @row_sep = row_sep
+ @fs = fs
+ @rs = rs
@dev = CSV::IOBuf.new(@io)
@idx = 0
if @dev[0] == 0xef and @dev[1] == 0xbb and @dev[2] == 0xbf
@@ -621,9 +584,8 @@ class CSV
private
def get_row(row)
- parsed_cells, next_idx =
- CSV.parse_row(@dev, @idx, row, @col_sep, @row_sep)
- if parsed_cells == 0 && next_idx == 0 && !@dev.is_eos?
+ parsed_cells, next_idx = CSV.parse_row(@dev, @idx, row, @fs, @rs)
+ if parsed_cells == 0 and next_idx == 0 and !@dev.is_eos?
raise IllegalFormatError.new
end
dropped = @dev.drop(next_idx)
@@ -667,40 +629,25 @@ class CSV
# outfile.close
#
class Writer
-
# Generate CSV. Given block is called with the writer instance.
- def Writer.generate(str_or_writable, col_sep = ?,, row_sep = nil)
- writer = Writer.create(str_or_writable, col_sep, row_sep)
+ def Writer.generate(str_or_writable, fs = ',', rs = nil)
+ writer = Writer.create(str_or_writable, fs, rs)
yield(writer)
writer.close
nil
end
# str_or_writable must handle '<<(string)'.
- def Writer.create(str_or_writable, col_sep = ?,, row_sep = nil)
- BasicWriter.new(str_or_writable, col_sep, row_sep)
+ def Writer.create(str_or_writable, fs = ',', rs = nil)
+ BasicWriter.new(str_or_writable, fs, rs)
end
# dump CSV stream to the device. argument must be an Array of String.
- def <<(ary)
- row = ary.collect { |item|
- if item.is_a?(Cell)
- item
- elsif (item.nil?)
- Cell.new('', true)
- else
- Cell.new(item.to_s, false)
- end
- }
- CSV.generate_row(row, row.size, @dev, @col_sep, @row_sep)
- self
- end
-
- # dump CSV stream to the device. argument must be an Array of CSV::Cell.
- def add_row(row)
- CSV.generate_row(row, row.size, @dev, @col_sep, @row_sep)
+ def <<(row)
+ CSV.generate_row(row, row.size, @dev, @fs, @rs)
self
end
+ alias add_row <<
def close
terminate
@@ -709,7 +656,7 @@ class CSV
private
def initialize(dev)
- raise RuntimeError.new('do not instantiate this class directly')
+ raise RuntimeError.new('Do not instanciate this class directly.')
end
def terminate
@@ -719,12 +666,10 @@ class CSV
class BasicWriter < Writer
-
- def initialize(str_or_writable, col_sep = ?,, row_sep = nil)
- @col_sep = col_sep
- @row_sep = row_sep
+ def initialize(str_or_writable, fs = ',', rs = nil)
+ @fs = fs
+ @rs = rs
@dev = str_or_writable
- @dev.binmode if @dev.respond_to?(:binmode)
@close_on_terminate = false
end
@@ -743,6 +688,7 @@ class CSV
end
end
+private
# Buffered stream.
#
@@ -756,7 +702,7 @@ class CSV
# end
#
# # define my own 'read' method.
- # # CAUTION: Returning nil means EndOfStream.
+ # # CAUTION: Returning nil means EnfOfStream.
# def read(size)
# @s.read(size)
# end
@@ -801,8 +747,7 @@ class CSV
# end
# end
#
- class StreamBuf # pure virtual. (do not instantiate it directly)
-
+ class StreamBuf
# get a char or a partial string from the stream.
# idx: index of a string to specify a start point of a string to get.
# unlike String instance, idx < 0 returns nil.
@@ -810,7 +755,7 @@ class CSV
# returns char at idx if n == nil.
# returns a partial string, from idx to (idx + n) if n != nil. at EOF,
# the string size could not equal to arg n.
- def [](idx, n = nil)
+ def [](idx, n = nil)
if idx < 0
return nil
end
@@ -838,11 +783,11 @@ class CSV
end
loc = my_offset + next_idx
if !n
- return @buf_list[my_buf][loc] # Fixnum of char code.
+ return @buf_list[my_buf][loc] # Fixnum of char code.
elsif (loc + n - 1 < buf_size(my_buf))
- return @buf_list[my_buf][loc, n] # String.
+ return @buf_list[my_buf][loc, n] # String.
else # should do loop insted of (tail) recursive call...
- res = @buf_list[my_buf][loc, BufSize]
+ res = @buf_list[my_buf][loc, BufSize]
size_added = buf_size(my_buf) - loc
if size_added > 0
idx += size_added
@@ -856,7 +801,7 @@ class CSV
end
end
alias get []
-
+
# drop a string from the stream.
# returns dropped size. at EOF, dropped size might not equals to arg n.
# Once you drop the head of the stream, access to the dropped part via []
@@ -867,7 +812,7 @@ class CSV
end
size_dropped = 0
while (n > 0)
- if (!@is_eos || (@cur_buf != @buf_tail_idx))
+ if !@is_eos or (@cur_buf != @buf_tail_idx)
if (@offset + n < buf_size(@cur_buf))
size_dropped += n
@offset += n
@@ -888,11 +833,11 @@ class CSV
end
size_dropped
end
-
+
def is_eos?
return idx_is_eos?(0)
end
-
+
# WARN: Do not instantiate this class directly. Define your own class
# which derives this class and define 'read' instance method.
def initialize
@@ -903,24 +848,23 @@ class CSV
add_buf
@cur_buf = @buf_tail_idx
end
-
+
protected
def terminate
while (rel_buf); end
end
-
+
# protected method 'read' must be defined in derived classes.
# CAUTION: Returning a string which size is not equal to 'size' means
- # EndOfStream. When it is not at EOS, you must block the callee, try to
+ # EnfOfStream. When it is not at EOS, you must block the callee, try to
# read and return the sized string.
def read(size) # raise EOFError
- raise NotImplementedError.new(
- 'method read must be defined in a derived class')
+ raise NotImplementedError.new('Method read must be defined in a derived class.')
end
-
+
private
-
+
def buf_size(idx)
@buf_list[idx].size
end
@@ -948,7 +892,7 @@ class CSV
true
end
end
-
+
def rel_buf
if (@cur_buf < 0)
return false
@@ -962,15 +906,14 @@ class CSV
return true
end
end
-
+
def idx_is_eos?(idx)
- (@is_eos && ((@cur_buf < 0) || (@cur_buf == @buf_tail_idx)))
+ (@is_eos and ((@cur_buf < 0) or (@cur_buf == @buf_tail_idx)))
end
-
+
BufSize = 1024 * 8
end
-
# Buffered IO.
#
# EXAMPLE
@@ -986,7 +929,7 @@ class CSV
@s = s
super()
end
-
+
def close
terminate
end
@@ -996,7 +939,7 @@ class CSV
def read(size)
@s.read(size)
end
-
+
def terminate
super()
end