* 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
This commit is contained in:
nahi 2004-05-18 12:34:33 +00:00
Родитель 75db654808
Коммит 34a553da2e
3 изменённых файлов: 471 добавлений и 619 удалений

Просмотреть файл

@ -1,3 +1,58 @@
Tue May 18 21:21:43 2004 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* 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.
Tue May 18 14:24:20 2004 why the lucky stiff <why@ruby-lang.org>
* lib/yaml.rb: added rdoc to beginning of lib.

Просмотреть файл

@ -1,4 +1,5 @@
# CSV -- module for generating/parsing CSV data.
# Copyright (C) 2000-2004 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>.
# $Id$
@ -8,99 +9,22 @@
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
@ -259,7 +193,6 @@ class CSV
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,134 +290,162 @@ 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 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
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
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
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 fs_idx == fs_size
fs_idx = 0
if cr
raise IllegalFormatError.new
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
true # ToDo: delete; dummy line for coverage
else
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
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
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
elsif c == row_sep_end
if state.equal?(:ST_DATA)
if cr
state = :ST_END
result_state = :DT_ROWSEP
cr = false
else
if quoted
cell.data << c.chr
state = :ST_DATA
else
state = :ST_END
result_state = :DT_ROWSEP
if fs_idx != 0
fs_idx = 0
end
if rs_idx != 0
rs_idx = 0
end
elsif state.equal?(:ST_QUOTE)
state = :ST_END
result_state = :DT_ROWSEP
if state.equal?(:ST_DATA) or state.equal?(:ST_START)
if cr
cr = false
raise IllegalFormatError
end
else # :ST_START
cell.is_null = true
state = :ST_END
result_state = :DT_ROWSEP
end
else
if state.equal?(:ST_DATA) || state.equal?(:ST_START)
if cr
raise IllegalFormatError.new
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
@ -485,12 +453,12 @@ class CSV
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,12 +525,11 @@ 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
@ -572,10 +539,9 @@ class CSV
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.
@ -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
@ -912,11 +857,10 @@ class CSV
# 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
@ -964,13 +908,12 @@ class CSV
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

Просмотреть файл

@ -1,5 +1,4 @@
require 'test/unit/testsuite'
require 'test/unit/testcase'
require 'test/unit'
require 'tempfile'
require 'fileutils'
@ -15,174 +14,20 @@ end
module CSVTestSupport
def d(data, is_null = false)
CSV::Cell.new(data.to_s, is_null)
end
end
class TestCSVCell < Test::Unit::TestCase
@@colData = ['', nil, true, false, 'foo', '!' * 1000]
def test_Cell_EQUAL # '=='
d1 = CSV::Cell.new('d', false)
d2 = CSV::Cell.new('d', false)
d3 = CSV::Cell.new('d', true)
d4 = CSV::Cell.new('d', true)
assert(d1 == d2, "Normal case.")
assert(d1 != d3, "RHS is null.")
assert(d4 != d1, "LHS is null.")
assert(d3 != d4, "Either is null.")
end
def test_Cell_match
d1 = CSV::Cell.new('d', false)
d2 = CSV::Cell.new('d', false)
d3 = CSV::Cell.new('d', true)
d4 = CSV::Cell.new('d', true)
assert(d1.match(d2), "Normal case.")
assert(!d1.match(d3), "RHS is null.")
assert(!d4.match(d1), "LHS is null.")
assert(d3.match(d4), "Either is null.")
end
def test_Cell_data
d = CSV::Cell.new()
@@colData.each do |v|
d.data = v
assert_equal(d.data, v, "Case: #{ v }.")
end
end
def test_Cell_data=
d = CSV::Cell.new()
@@colData.each do |v|
d.data = v
assert_equal(d.data, v, "Case: #{ v }.")
end
end
def test_Cell_is_null
d = CSV::Cell.new()
d.is_null = true
assert_equal(d.is_null, true, "Case: true.")
d.is_null = false
assert_equal(d.is_null, false, "Case: false.")
end
def test_Cell_is_null=
d = CSV::Cell.new()
d.is_null = true
assert_equal(d.is_null, true, "Case: true.")
d.is_null = false
assert_equal(d.is_null, false, "Case: false.")
end
def test_Cell_s_new
d1 = CSV::Cell.new()
assert_equal(d1.data, '', "Default: data.")
assert_equal(d1.is_null, true, "Default: is_null.")
@@colData.each do |v|
d = CSV::Cell.new(v)
assert_equal(d.data, v, "Data: #{ v }.")
end
d2 = CSV::Cell.new(nil, true)
assert_equal(d2.is_null, true, "Data: true.")
d3 = CSV::Cell.new(nil, false)
assert_equal(d3.is_null, false, "Data: false.")
end
def test_to_str
d = CSV::Cell.new("foo", false)
assert_equal("foo", d.to_str)
assert(/foo/ =~ d)
d = CSV::Cell.new("foo", true)
begin
d.to_str
assert(false)
rescue
# NoMethodError or NameError
assert(true)
end
end
def test_to_s
d = CSV::Cell.new("foo", false)
assert_equal("foo", d.to_s)
assert_equal("foo", "#{d}")
d = CSV::Cell.new("foo", true)
assert_equal("", d.to_s)
assert_equal("", "#{d}")
end
end
class TestCSVRow < Test::Unit::TestCase
include CSVTestSupport
def test_Row_s_match
c1 = CSV::Row[d(1), d(2), d(3)]
c2 = CSV::Row[d(1, false), d(2, false), d(3, false)]
assert(c1.match(c2), "Normal case.")
c1 = CSV::Row[d(1), d('foo', true), d(3)]
c2 = CSV::Row[d(1, false), d('bar', true), d(3, false)]
assert(c1.match(c2), "Either is null.")
c1 = CSV::Row[d(1), d('foo', true), d(3)]
c2 = CSV::Row[d(1, false), d('bar', false), d(3, false)]
assert(!c1.match(c2), "LHS is null.")
c1 = CSV::Row[d(1), d('foo'), d(3)]
c2 = CSV::Row[d(1, false), d('bar', true), d(3, false)]
assert(!c1.match(c2), "RHS is null.")
c1 = CSV::Row[d(1), d('', true), d(3)]
c2 = CSV::Row[d(1, false), d('', true), d(3, false)]
assert(c1.match(c2), "Either is null(empty data).")
c1 = CSV::Row[d(1), d('', true), d(3)]
c2 = CSV::Row[d(1, false), d('', false), d(3, false)]
assert(!c1.match(c2), "LHS is null(empty data).")
c1 = CSV::Row[d(1), d(''), d(3)]
c2 = CSV::Row[d(1, false), d('', true), d(3, false)]
assert(!c1.match(c2), "RHS is null(empty data).")
c1 = CSV::Row[]
c2 = CSV::Row[]
assert(c1.match(c2))
c1 = CSV::Row[]
c2 = CSV::Row[d(1)]
assert(!c1.match(c2))
end
def test_Row_to_a
r = CSV::Row[d(1), d(2), d(3)]
assert_equal(['1', '2', '3'], r.to_a, 'Normal case')
r = CSV::Row[d(1)]
assert_equal(['1'], r.to_a, '1 item')
r = CSV::Row[d(nil, true), d(2), d(3)]
assert_equal([nil, '2', '3'], r.to_a, 'Null in data')
r = CSV::Row[d(nil, true), d(nil, true), d(nil, true)]
assert_equal([nil, nil, nil], r.to_a, 'Nulls')
r = CSV::Row[d(nil, true)]
assert_equal([nil], r.to_a, '1 Null')
r = CSV::Row[]
assert_equal([], r.to_a, 'Empty')
def d(data)
data
end
end
class TestCSV < Test::Unit::TestCase
file = Tempfile.new("crlf")
file << "\n"
file.open
file.binmode
RSEP = file.read
file.close
include CSVTestSupport
class << self
@ -221,17 +66,17 @@ class TestCSV < Test::Unit::TestCase
}
@@fullCSVData = {
[d('', true)] => '',
[d(nil)] => '',
[d('')] => '""',
[d('', true), d('', true)] => ',',
[d('', true), d('', true), d('', true)] => ',,',
[d(nil), d(nil)] => ',',
[d(nil), d(nil), d(nil)] => ',,',
[d('foo')] => 'foo',
[d('foo'), d('bar')] => 'foo,bar',
[d('foo'), d('"bar"'), d('baz')] => 'foo,"""bar""",baz',
[d('foo'), d('foo,bar'), d('baz')] => 'foo,"foo,bar",baz',
[d('foo'), d('""'), d('baz')] => 'foo,"""""",baz',
[d('foo'), d(''), d('baz')] => 'foo,"",baz',
[d('foo'), d('', true), d('baz')] => 'foo,,baz',
[d('foo'), d(nil), d('baz')] => 'foo,,baz',
[d('foo'), d("\r"), d('baz')] => "foo,\"\r\",baz",
[d('foo'), d("\n"), d('baz')] => "foo,\"\n\",baz",
[d('foo'), d("\r\n"), d('baz')] => "foo,\"\r\n\",baz",
@ -259,7 +104,7 @@ class TestCSV < Test::Unit::TestCase
end
def sepConv(srcStr, srcSep, destSep, row_sep = nil)
rows = CSV::Row.new
rows = []
cols, idx = CSV.parse_row(srcStr, 0, rows, srcSep, row_sep)
destStr = ''
cols = CSV.generate_row(rows, rows.size, destStr, destSep, row_sep)
@ -278,13 +123,13 @@ public
@bomfile = File.join(@tmpdir, "bom.csv")
@macfile = File.join(@tmpdir, "mac.csv")
CSV.open(@infile, "w") do |writer|
CSV.open(@infile, "wb") do |writer|
@@fullCSVDataArray.each do |row|
writer.add_row(row)
end
end
CSV.open(@infiletsv, "w", ?\t) do |writer|
CSV.open(@infiletsv, "wb", ?\t) do |writer|
@@fullCSVDataArray.each do |row|
writer.add_row(row)
end
@ -317,11 +162,11 @@ public
first = true
ret = reader.each { |row|
if first
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
first = false
end
expected = expectedArray.shift
assert(row.match(expected))
assert_equal(expected, row)
}
assert_nil(ret, "Return is nil")
assert(expectedArray.empty?)
@ -352,10 +197,10 @@ public
@@fullCSVDataArray.each do |expected|
actual = reader.shift
if first
assert_instance_of(CSV::Row, actual)
assert_instance_of(Array, actual)
first = false
end
assert(actual.match(expected))
assert_equal(expected, actual)
checked += 1
end
assert(checked == @@fullCSVDataArray.size)
@ -445,7 +290,7 @@ public
file << "\"\r\n\",\"\r\",\"\n\"\r1,2,3"
file.close
file = File.open(@outfile, "r") # not "rb"
file = File.open(@outfile, "rb")
begin
reader = CSV::IOReader.new(file, ?,, ?\r)
assert_equal(["\r\n", "\r", "\n"], reader.shift.to_a)
@ -454,23 +299,34 @@ public
ensure
file.close
end
file = File.open(@outfile, "r") # not "rb"
begin
lfincell = (RSEP == "\n" ? "\r\n" : "\n")
reader = CSV::IOReader.new(file, ?,, ?\r)
assert_equal([lfincell, "\r", "\n"], reader.shift.to_a)
assert_equal(["1", "2", "3"], reader.shift.to_a)
reader.close
ensure
file.close
end
end
def test_Reader_s_parse
ret = CSV::Reader.parse("a,b,c") { |row|
assert_instance_of(CSV::Row, row, "Block parameter")
assert_instance_of(Array, row, "Block parameter")
}
assert_nil(ret, "Return is nil")
ret = CSV::Reader.parse("a;b;c", ?;) { |row|
assert_instance_of(CSV::Row, row, "Block parameter")
assert_instance_of(Array, row, "Block parameter")
}
file = Tempfile.new("in.csv")
file << "a,b,c"
file.open
ret = CSV::Reader.parse(file) { |row|
assert_instance_of(CSV::Row, row, "Block parameter")
assert_instance_of(Array, row, "Block parameter")
}
assert_nil(ret, "Return is nil")
@ -478,7 +334,7 @@ public
file << "a,b,c"
file.open
ret = CSV::Reader.parse(file, ?,) { |row|
assert_instance_of(CSV::Row, row, "Block parameter")
assert_instance_of(Array, row, "Block parameter")
}
# Illegal format.
@ -536,38 +392,38 @@ public
file.open
file.binmode
str = file.read
assert_equal("a,b,c\r\n,e,f\r\n,,\"\"\r\n", str, 'Normal')
assert_equal("a,b,c#{RSEP},e,f#{RSEP},,\"\"#{RSEP}", str, 'Normal')
file = Tempfile.new("out2.csv")
CSV::Writer.generate(file) do |writer|
ret = writer << [d('a'), d('b'), d('c')]
assert_instance_of(CSV::BasicWriter, ret, 'Return is self')
writer << [d(nil, true), d('e'), d('f')] << [d(nil, true), d(nil, true), d('')]
writer << [d(nil), d('e'), d('f')] << [d(nil), d(nil), d('')]
end
file.open
file.binmode
str = file.read
assert_equal("a,b,c\r\n,e,f\r\n,,\"\"\r\n", str, 'Normal')
assert_equal("a,b,c#{RSEP},e,f#{RSEP},,\"\"#{RSEP}", str, 'Normal')
end
def test_Writer_add_row
file = Tempfile.new("out.csv")
CSV::Writer.generate(file) do |writer|
ret = writer.add_row(
[d('a', false), d('b', false), d('c', false)])
[d('a'), d('b'), d('c')])
assert_instance_of(CSV::BasicWriter, ret, 'Return is self')
writer.add_row(
[d('dummy', true), d('e', false), d('f', false)]
[d(nil), d('e'), d('f')]
).add_row(
[d('a', true), d('b', true), d('', false)]
[d(nil), d(nil), d('')]
)
end
file.open
file.binmode
str = file.read
assert_equal("a,b,c\r\n,e,f\r\n,,\"\"\r\n", str, 'Normal')
assert_equal("a,b,c#{RSEP},e,f#{RSEP},,\"\"#{RSEP}", str, 'Normal')
end
def test_Writer_close
@ -606,7 +462,7 @@ public
file = File.open(@outfile, "rb")
str = file.read
file.close
assert_equal("\"\r\n\",\"\r\",\"\n\"\r1,2,3\r", str)
assert_equal("\"\r#{RSEP}\",\"\r\",\"#{RSEP}\"\r1,2,3\r", str)
end
#### CSV unit test
@ -633,12 +489,12 @@ public
reader.close
CSV.open(@infile, "r") do |row|
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
break
end
CSV.open(@infiletsv, "r", ?\t) do |row|
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
break
end
@ -686,12 +542,12 @@ public
reader.close
CSV.parse(@infile) do |row|
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
break
end
CSV.parse(@infiletsv, ?\t) do |row|
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
break
end
@ -776,12 +632,12 @@ public
@@simpleCSVData.each do |col, str|
buf = CSV.generate_line(col, ?;)
assert_equal(str + "\r\n", ssv2csv(buf))
assert_equal(str + "\n", ssv2csv(buf))
end
@@simpleCSVData.each do |col, str|
buf = CSV.generate_line(col, ?\t)
assert_equal(str + "\r\n", tsv2csv(buf))
assert_equal(str + "\n", tsv2csv(buf))
end
end
@ -789,17 +645,17 @@ public
buf = ''
cols = CSV.generate_row([], 0, buf)
assert_equal(0, cols)
assert_equal("\r\n", buf, "Extra boundary check.")
assert_equal("\n", buf, "Extra boundary check.")
buf = ''
cols = CSV.generate_row([], 0, buf, ?;)
assert_equal(0, cols)
assert_equal("\r\n", buf, "Extra boundary check.")
assert_equal("\n", buf, "Extra boundary check.")
buf = ''
cols = CSV.generate_row([], 0, buf, ?\t)
assert_equal(0, cols)
assert_equal("\r\n", buf, "Extra boundary check.")
assert_equal("\n", buf, "Extra boundary check.")
buf = ''
cols = CSV.generate_row([], 0, buf, ?\t, ?|)
@ -807,64 +663,64 @@ public
assert_equal("|", buf, "Extra boundary check.")
buf = ''
cols = CSV.generate_row([d(1)], 2, buf)
cols = CSV.generate_row([d('1')], 2, buf)
assert_equal('1,', buf)
buf = ''
cols = CSV.generate_row([d(1)], 2, buf, ?;)
cols = CSV.generate_row([d('1')], 2, buf, ?;)
assert_equal('1;', buf)
buf = ''
cols = CSV.generate_row([d(1)], 2, buf, ?\t)
cols = CSV.generate_row([d('1')], 2, buf, ?\t)
assert_equal("1\t", buf)
buf = ''
cols = CSV.generate_row([d(1)], 2, buf, ?\t, ?|)
cols = CSV.generate_row([d('1')], 2, buf, ?\t, ?|)
assert_equal("1\t", buf)
buf = ''
cols = CSV.generate_row([d(1), d(2)], 1, buf)
assert_equal("1\r\n", buf)
buf = ''
cols = CSV.generate_row([d(1), d(2)], 1, buf, ?;)
assert_equal("1\r\n", buf)
buf = ''
cols = CSV.generate_row([d(1), d(2)], 1, buf, ?\t)
assert_equal("1\r\n", buf)
buf = ''
cols = CSV.generate_row([d(1), d(2)], 1, buf, ?\t, ?\n)
cols = CSV.generate_row([d('1'), d('2')], 1, buf)
assert_equal("1\n", buf)
buf = ''
cols = CSV.generate_row([d(1), d(2)], 1, buf, ?\t, ?\r)
cols = CSV.generate_row([d('1'), d('2')], 1, buf, ?;)
assert_equal("1\n", buf)
buf = ''
cols = CSV.generate_row([d('1'), d('2')], 1, buf, ?\t)
assert_equal("1\n", buf)
buf = ''
cols = CSV.generate_row([d('1'), d('2')], 1, buf, ?\t, ?\n)
assert_equal("1\n", buf)
buf = ''
cols = CSV.generate_row([d('1'), d('2')], 1, buf, ?\t, ?\r)
assert_equal("1\r", buf)
buf = ''
cols = CSV.generate_row([d(1), d(2)], 1, buf, ?\t, ?|)
cols = CSV.generate_row([d('1'), d('2')], 1, buf, ?\t, ?|)
assert_equal("1|", buf)
@@fullCSVData.each do |col, str|
buf = ''
cols = CSV.generate_row(col, col.size, buf)
assert_equal(col.size, cols)
assert_equal(str + "\r\n", buf)
assert_equal(str + "\n", buf)
end
@@fullCSVData.each do |col, str|
buf = ''
cols = CSV.generate_row(col, col.size, buf, ?;)
assert_equal(col.size, cols)
assert_equal(str + "\r\n", ssv2csv(buf))
assert_equal(str + "\n", ssv2csv(buf))
end
@@fullCSVData.each do |col, str|
buf = ''
cols = CSV.generate_row(col, col.size, buf, ?\t)
assert_equal(col.size, cols)
assert_equal(str + "\r\n", tsv2csv(buf))
assert_equal(str + "\n", tsv2csv(buf))
end
# row separator
@ -889,7 +745,7 @@ public
colsToBe = 0
@@fullCSVData.each do |col, str|
cols += CSV.generate_row(col, col.size, buf)
toBe << str << "\r\n"
toBe << str << "\n"
colsToBe += col.size
end
assert_equal(colsToBe, cols)
@ -902,8 +758,8 @@ public
@@fullCSVData.each do |col, str|
lineBuf = ''
cols += CSV.generate_row(col, col.size, lineBuf, ?;)
buf << ssv2csv(lineBuf) << "\r\n"
toBe << ssv2csv(lineBuf) << "\r\n"
buf << ssv2csv(lineBuf) << "\n"
toBe << ssv2csv(lineBuf) << "\n"
colsToBe += col.size
end
assert_equal(colsToBe, cols)
@ -916,8 +772,8 @@ public
@@fullCSVData.each do |col, str|
lineBuf = ''
cols += CSV.generate_row(col, col.size, lineBuf, ?\t)
buf << tsv2csv(lineBuf) << "\r\n"
toBe << tsv2csv(lineBuf) << "\r\n"
buf << tsv2csv(lineBuf) << "\n"
toBe << tsv2csv(lineBuf) << "\n"
colsToBe += col.size
end
assert_equal(colsToBe, cols)
@ -941,7 +797,7 @@ public
def test_s_parse_line
@@simpleCSVData.each do |col, str|
row = CSV.parse_line(str)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(col.size, row.size)
assert_equal(col, row)
end
@ -949,214 +805,214 @@ public
@@simpleCSVData.each do |col, str|
str = csv2ssv(str)
row = CSV.parse_line(str, ?;)
assert_instance_of(CSV::Row, row)
assert_equal(col.size, row.size)
assert_equal(col, row)
assert_instance_of(Array, row)
assert_equal(col.size, row.size, str.inspect)
assert_equal(col, row, str.inspect)
end
@@simpleCSVData.each do |col, str|
str = csv2tsv(str)
row = CSV.parse_line(str, ?\t)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(col.size, row.size)
assert_equal(col, row)
end
# Illegal format.
buf = CSV::Row.new
buf = []
row = CSV.parse_line("a,b,\"c\"\ra")
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
buf = CSV::Row.new
buf = Array.new
row = CSV.parse_line("a;b;\"c\"\ra", ?;)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
buf = CSV::Row.new
buf = Array.new
row = CSV.parse_line("a\tb\t\"c\"\ra", ?\t)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("a,b\"")
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("a;b\"", ?;)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("a\tb\"", ?\t)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("\"a,b\"\r,")
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("\"a;b\"\r;", ?;)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("\"a\tb\"\r\t", ?\t)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("\"a,b\"\r\"")
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("\"a;b\"\r\"", ?;)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
row = CSV.parse_line("\"a\tb\"\r\"", ?\t)
assert_instance_of(CSV::Row, row)
assert_instance_of(Array, row)
assert_equal(0, row.size)
end
def test_s_parse_row
@@fullCSVData.each do |col, str|
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row(str + "\r\n", 0, buf)
assert_equal(cols, buf.size, "Reported size.")
assert_equal(col.size, buf.size, "Size.")
assert(buf.match(col))
assert_equal(col, buf, str.inspect)
buf = CSV::Row.new
cols, idx = CSV.parse_row(str + "\n", 0, buf)
buf = Array.new
cols, idx = CSV.parse_row(str + "\n", 0, buf, ?,, ?\n)
assert_equal(cols, buf.size, "Reported size.")
assert_equal(col.size, buf.size, "Size.")
assert(buf.match(col))
assert_equal(col, buf, str.inspect)
# separator: |
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row(str + "|", 0, buf, ?,)
assert(!buf.match(col))
buf = CSV::Row.new
assert_not_equal(col, buf)
buf = Array.new
cols, idx = CSV.parse_row(str + "|", 0, buf, ?,, ?|)
assert_equal(cols, buf.size, "Reported size.")
assert_equal(col.size, buf.size, "Size.")
assert(buf.match(col))
assert_equal(col, buf, str.inspect)
end
@@fullCSVData.each do |col, str|
str = csv2ssv(str)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row(str + "\r\n", 0, buf, ?;)
assert_equal(cols, buf.size, "Reported size.")
assert_equal(col.size, buf.size, "Size.")
assert(buf.match(col))
assert_equal(col, buf, str)
end
@@fullCSVData.each do |col, str|
str = csv2tsv(str)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row(str + "\r\n", 0, buf, ?\t)
assert_equal(cols, buf.size, "Reported size.")
assert_equal(col.size, buf.size, "Size.")
assert(buf.match(col))
assert_equal(col, buf, str)
end
@@fullCSVData.each do |col, str|
str = csv2tsv(str, ?|)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row(str + "|", 0, buf, ?\t, ?|)
assert_equal(cols, buf.size, "Reported size.")
assert_equal(col.size, buf.size, "Size.")
assert(buf.match(col), str)
assert_equal(col, buf, str)
end
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a,b,\"c\r\"", 0, buf)
assert_equal(["a", "b", "c\r"], buf.to_a)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a;b;\"c\r\"", 0, buf, ?;)
assert_equal(["a", "b", "c\r"], buf.to_a)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a\tb\t\"c\r\"", 0, buf, ?\t)
assert_equal(["a", "b", "c\r"], buf.to_a)
buf = CSV::Row.new
cols, idx = CSV.parse_row("a,b,c\n", 0, buf)
buf = Array.new
cols, idx = CSV.parse_row("a,b,c\n", 0, buf, ?,, ?\n)
assert_equal(["a", "b", "c"], buf.to_a)
buf = CSV::Row.new
cols, idx = CSV.parse_row("a\tb\tc\n", 0, buf, ?\t)
buf = Array.new
cols, idx = CSV.parse_row("a\tb\tc\n", 0, buf, ?\t, ?\n)
assert_equal(["a", "b", "c"], buf.to_a)
# Illegal format.
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a,b,c\"", 0, buf)
assert_equal(0, cols, "Illegal format; unbalanced double-quote.")
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a;b;c\"", 0, buf, ?;)
assert_equal(0, cols, "Illegal format; unbalanced double-quote.")
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a,b,\"c\"\ra", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a,b,\"c\"\ra", 0, buf, ?;)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a,b\"", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a;b\"", 0, buf, ?;)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("\"a,b\"\r,", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a\r,", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a\r", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a\rbc", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a\r\"\"", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("a\r\rabc,", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("\"a;b\"\r;", 0, buf, ?;)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("\"a,b\"\r\"", 0, buf)
assert_equal(0, cols)
assert_equal(0, idx)
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row("\"a;b\"\r\"", 0, buf, ?;)
assert_equal(0, cols)
assert_equal(0, idx)
@ -1168,11 +1024,11 @@ public
# String "" is not allowed.
next
end
buf = CSV::Row.new
buf = Array.new
cols, idx = CSV.parse_row(str, 0, buf)
assert_equal(col.size, cols, "Reported size.")
assert_equal(col.size, buf.size, "Size.")
assert(buf.match(col))
assert_equal(col, buf)
end
end
@ -1185,7 +1041,7 @@ public
end
idx = 0
cols = 0
parsed = CSV::Row.new
parsed = Array.new
parsedCols = 0
begin
cols, idx = CSV.parse_row(buf, idx, parsed)
@ -1193,7 +1049,7 @@ public
end while cols > 0
assert_equal(toBe.size, parsedCols)
assert_equal(toBe.size, parsed.size)
assert(parsed.match(toBe))
assert_equal(toBe, parsed)
buf = ''
toBe = []
@ -1203,15 +1059,15 @@ public
end
idx = 0
cols = 0
parsed = CSV::Row.new
parsed = Array.new
parsedCols = 0
begin
cols, idx = CSV.parse_row(buf, idx, parsed)
cols, idx = CSV.parse_row(buf, idx, parsed, ?,, ?\n)
parsedCols += cols
end while cols > 0
assert_equal(toBe.size, parsedCols)
assert_equal(toBe.size, parsed.size)
assert(parsed.match(toBe))
assert_equal(toBe, parsed)
buf = ''
toBe = []
@ -1223,15 +1079,15 @@ public
end
idx = 0
cols = 0
parsed = CSV::Row.new
parsed = Array.new
parsedCols = 0
begin
cols, idx = CSV.parse_row(buf, idx, parsed)
cols, idx = CSV.parse_row(buf, idx, parsed, ?,, ?\n)
parsedCols += cols
end while cols > 0
assert_equal(toBe.size, parsedCols)
assert_equal(toBe.size, parsed.size)
assert(parsed.match(toBe))
assert_equal(toBe, parsed)
buf = ''
toBe = []
@ -1241,7 +1097,7 @@ public
end
idx = 0
cols = 0
parsed = CSV::Row.new
parsed = []
parsedCols = 0
begin
cols, idx = CSV.parse_row(buf, idx, parsed, ?,, ?|)
@ -1249,7 +1105,7 @@ public
end while cols > 0
assert_equal(toBe.size, parsedCols)
assert_equal(toBe.size, parsed.size)
assert(parsed.match(toBe))
assert_equal(toBe, parsed)
end
def test_utf8
@ -1280,25 +1136,33 @@ public
CSV.open(@macfile, "r") do |row|
rows << row.to_a
end
assert_equal([["Avenches", "aus Umgebung\r\"Bad Hersfeld", "Ausgrabung"]], rows)
end
rows = []
file = File.open(@macfile)
begin
CSV::Reader.parse(file, ?,, ?\r) do |row|
rows << row.to_a
end
assert_equal([["Avenches", "aus Umgebung"], ["Bad Hersfeld", "Ausgrabung"]], rows)
ensure
file.close
end
rows = []
file = File.open(@macfile)
begin
assert_raises(CSV::IllegalFormatError) do
CSV::Reader.parse(file, ?,) do |row|
rows << row.to_a
end
assert_equal([["Avenches", "aus Umgebung\r\"Bad Hersfeld", "Ausgrabung"]], rows)
end
ensure
file.close
end
end
#### CSV unit test
@ -1678,8 +1542,8 @@ public
#
def test_s_parseAndCreate
colSize = 8
csvStr = "foo,!!!foo!!!,!foo,bar!,!!!!!!,!!,,!\r!,!\r\n!\r\nNaHi,!!!Na!!!,!Na,Hi!,!\r.\n!,!\r\n\n!,!!!!,!\n!,!\r\n!".gsub!('!', '"')
csvStrTerminated = csvStr + "\r\n"
csvStr = "foo,!!!foo!!!,!foo,bar!,!!!!!!,!!,,!\r!,!\r\n!\nNaHi,!!!Na!!!,!Na,Hi!,!\r.\n!,!\r\n\n!,!!!!,!\n!,!\r\n!".gsub!('!', '"')
csvStrTerminated = csvStr + "\n"
myStr = csvStr.dup
res1 = []; res2 = []
@ -1708,19 +1572,9 @@ public
buf = ''
CSV::Writer.generate(buf) do |writer|
parsed.each do |row|
writer << row.collect { |e| e.is_null ? nil : e.data }
writer << row
end
end
assert_equal(csvStrTerminated, buf)
end
end
if $0 == __FILE__
suite = Test::Unit::TestSuite.new('CSV')
ObjectSpace.each_object(Class) do |klass|
suite << klass.suite if (Test::Unit::TestCase > klass)
end
require 'test/unit/ui/console/testrunner'
Test::Unit::UI::Console::TestRunner.run(suite).passed?
end