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 }