NAKAMURA, Hiroshi wrote:

> Of cource I don't think everyone needs this "complexity" (and
slowness).
>   Regexp based approach is very useful, too.  (I often do that.)


Added a bit of complexity in the form of error-checking.


z  ## Read, parse, and create csv records.
z  ## 2005-02-01, v. 2.
z  ## Added a pinch of optional error-checking.
z
z  # The program conforms to the csv specification at this site:
z  # http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
z  # The only extra is that you can change the field-separator.
z  # For a field-separator other than a comma, for example
z  # a semicolon:
z  #   ";".is_fs
z  #
z  # After a record has been read and parsed,
z  # $csv_s contains the record in raw string format.
z  #
z  # If $csv_error_check == true, fields will be checked
z  # for improperly escaped double-quotes.
z
z  class Array
z    def to_csv
z      ",".is_fs   if $csv_fs.nil?
z      s = ''
z      self.map { |item|
z        str = item.to_s
z        # Quote the string if it contains the field-separator or
z        # a " or a newline, or if it has leading or
z        # trailing whitespace.
z        if str.index($csv_fs) or /^\s|"|\n|\s$/.match(str)
z          str = '"' + str.gsub( /"/, '""' ) + '"'
z        end
z        str
z      }.join($csv_fs)
z    end
z    def unescape
z      if $csv_error_check
z        self.map{ |x|
z          # Check for improperly escaped double-quotes.
z          raise "Bad field: #{x}" if x.gsub(/""/,'').index('"')
z          x.gsub( /""/, '"' ) }
z      else
z        self.map{|x| x.gsub( /""/, '"' ) }
z      end
z    end
z  end
z
z  class String
z    # Set regexp for parse_csv.
z    # self is the field-separator, which must be
z    # a single character.
z    def is_fs
z      $csv_fs = self
z      if "^" == $csv_fs
z        fs = "\\^"
z      else
z        fs = $csv_fs
z      end
z      $csv_re = \
z        ## Assumes embedded quotes are escaped as "".
z        %r{  \s*
z             (?:
z                 "(  [^"]*  (?:  "" [^"]*  )*  )"  |
z                  ( .*? )
z             )
z             \s*
z             [#{fs}]
z          }mx
z    end
z
z    def parse_string
z      ",".is_fs   if $csv_fs.nil?
z      (self + $csv_fs).scan( $csv_re ).flatten.compact.unescape
z    end
z
z  end
z
z  def get_rec( file )
z    $csv_s = ""
z    begin
z      if file.eof?
z        raise "The csv file is malformed." if $csv_s.size>0
z        return nil
z      end
z      $csv_s += file.gets
z    end  until $csv_s.count( '"' ) % 2 == 0
z    $csv_s.chomp!
z    $csv_s.parse_string
z  end
z
z  $csv_error_check = true
z
z  while  rec = get_rec( ARGF )
z    puts "----------------"
z    puts $csv_s
z    p rec
z    puts rec.to_csv
z  end