summaryrefslogtreecommitdiff
path: root/lib/csv.rb
diff options
context:
space:
mode:
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