diff options
author | nahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-05-18 12:34:33 +0000 |
---|---|---|
committer | nahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-05-18 12:34:33 +0000 |
commit | 34a553da2ee21e96999c150f80b7231d474a48d3 (patch) | |
tree | dd3b69b5279a390e046ff8b2f7eed13c2614298e /lib | |
parent | 75db654808404ad6f7ec79c3f9ae6fda8a3c827e (diff) | |
download | ruby-34a553da2ee21e96999c150f80b7231d474a48d3.tar.gz |
* 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')
-rw-r--r-- | lib/csv.rb | 545 |
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 |