NAKAMURA, Hiroshi  wrote

>#  1      2       3         4       5        6      7    8
># +------+-------+---------+-------+--------+------+----+------+
># | foo  | "foo" | foo,bar | ""    |(empty) |(null)| \r | \r\n |
># +------+-------+---------+-------+--------+------+----+------+
># | NaHi | "Na"  | Na,Hi   | \r.\n | \r\n\n | "    | \n | \r\n |
># +------+-------+---------+-------+--------+------+----+------+
>#

foo  | "foo" | foo,bar | ""    |        |   | \r | \r\n |
NaHi | "Na"  | Na,Hi   | \r.\n | \r\n\n | " | \n | \r\n |

is what my program produces.  The only difference is that
I recognize fields that are empty strings, but not NULL fields;
i.e., these two csv records are equivalent:

foo,"",bar
foo,,bar

To produce the above output, I wrote the test string to a file
and ran this:

z  recs = []
z  while  rec = get_rec( ARGF )
z    recs << rec.map { |field|
z      field.gsub!( /\r|\n/ ) {|s| ("\r"==s) ? '\r' : '\n' }
z      field
z    }
z  end
z  recs.each_with_index { |rec,ri|
z    rec.each_with_index { |field,fi|
z      width = [field.size, recs[(ri-1).abs][fi].size ].max
z      printf "%-#{width}s | ", field
z    }
z    puts ""
z  }