Class | CSV |
In: |
lib/csv.rb
|
Parent: | Object |
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.
# File lib/csv.rb, line 93 93: def CSV.foreach(path, rs = nil, &block) 94: open_reader(path, 'r', ',', rs, &block) 95: end
# File lib/csv.rb, line 110 110: def CSV.generate(path, fs = nil, rs = nil, &block) 111: open_writer(path, 'w', fs, rs, &block) 112: end
Create a line from cells. each cell is stringified by to_s.
# File lib/csv.rb, line 160 160: def CSV.generate_line(row, fs = nil, rs = nil) 161: if row.size == 0 162: return '' 163: end 164: fs ||= ',' 165: if fs.is_a?(Fixnum) 166: fs = fs.chr 167: end 168: if !rs.nil? and rs.is_a?(Fixnum) 169: rs = rs.chr 170: end 171: res_type = :DT_COLSEP 172: result_str = '' 173: idx = 0 174: while true 175: generate_body(row[idx], result_str, fs, rs) 176: idx += 1 177: if (idx == row.size) 178: break 179: end 180: generate_separator(:DT_COLSEP, result_str, fs, rs) 181: end 182: result_str 183: end
Convert a line from cells data to string. Consider using CSV.generate_line instead. To generate multi-row CSV string, see EXAMPLE below.
EXAMPLE
row1 = ['a', 'b'] row2 = ['c', 'd'] row3 = ['e', 'f'] src = [row1, row2, row3] buf = '' src.each do |row| parsed_cells = CSV.generate_row(row, 2, buf) puts "Created #{ parsed_cells } cells." end p buf
ARGS
src: an Array of String to be converted to CSV string. Must respond to 'size' and '[](idx)'. src[idx] must return String. cells: num of cells in a line. out_dev: buffer for generated CSV string. Must respond to '<<(string)'. col_sep: Column separator. ?, by default. If you want to separate fields with semicolon, give ?; here. row_sep: Row separator. nil by default. nil means "\r\n or \n". If you want to separate records with \r, give ?\r here.
RETURNS
parsed_cells: num of converted cells.
# File lib/csv.rb, line 271 271: def CSV.generate_row(src, cells, out_dev, fs = nil, rs = nil) 272: fs ||= ',' 273: if fs.is_a?(Fixnum) 274: fs = fs.chr 275: end 276: if !rs.nil? and rs.is_a?(Fixnum) 277: rs = rs.chr 278: end 279: src_size = src.size 280: if (src_size == 0) 281: if cells == 0 282: generate_separator(:DT_ROWSEP, out_dev, fs, rs) 283: end 284: return 0 285: end 286: res_type = :DT_COLSEP 287: parsed_cells = 0 288: generate_body(src[parsed_cells], out_dev, fs, rs) 289: parsed_cells += 1 290: while ((parsed_cells < cells) and (parsed_cells != src_size)) 291: generate_separator(:DT_COLSEP, out_dev, fs, rs) 292: generate_body(src[parsed_cells], out_dev, fs, rs) 293: parsed_cells += 1 294: end 295: if (parsed_cells == cells) 296: generate_separator(:DT_ROWSEP, out_dev, fs, rs) 297: else 298: generate_separator(:DT_COLSEP, out_dev, fs, rs) 299: end 300: parsed_cells 301: end
Open a CSV formatted file for reading or writing.
For reading.
EXAMPLE 1
CSV.open('csvfile.csv', 'r') do |row| p row end
EXAMPLE 2
reader = CSV.open('csvfile.csv', 'r') row1 = reader.shift row2 = reader.shift if row2.empty? p 'row2 not find.' end reader.close
ARGS
filename: filename to parse. col_sep: Column separator. ?, by default. If you want to separate fields with semicolon, give ?; here. row_sep: Row separator. nil by default. nil means "\r\n or \n". If you want to separate records with \r, give ?\r here.
RETURNS
reader instance. To get parse result, see CSV::Reader#each.
For writing.
EXAMPLE 1
CSV.open('csvfile.csv', 'w') do |writer| writer << ['r1c1', 'r1c2'] writer << ['r2c1', 'r2c2'] writer << [nil, nil] end
EXAMPLE 2
writer = CSV.open('csvfile.csv', 'w') writer << ['r1c1', 'r1c2'] << ['r2c1', 'r2c2'] << [nil, nil] writer.close
ARGS
filename: filename to generate. col_sep: Column separator. ?, by default. If you want to separate fields with semicolon, give ?; here. row_sep: Row separator. nil by default. nil means "\r\n or \n". If you want to separate records with \r, give ?\r here.
RETURNS
writer instance. See CSV::Writer#<< and CSV::Writer#add_row to know how to generate CSV string.
# File lib/csv.rb, line 83 83: def CSV.open(path, mode, fs = nil, rs = nil, &block) 84: if mode == 'r' or mode == 'rb' 85: open_reader(path, mode, fs, rs, &block) 86: elsif mode == 'w' or mode == 'wb' 87: open_writer(path, mode, fs, rs, &block) 88: else 89: raise ArgumentError.new("'mode' must be 'r', 'rb', 'w', or 'wb'") 90: end 91: end
Parse lines from given string or stream. Return rows as an Array of Arrays.
# File lib/csv.rb, line 115 115: def CSV.parse(str_or_readable, fs = nil, rs = nil, &block) 116: if File.exist?(str_or_readable) 117: STDERR.puts("CSV.parse(filename) is deprecated." + 118: " Use CSV.open(filename, 'r') instead.") 119: return open_reader(str_or_readable, 'r', fs, rs, &block) 120: end 121: if block 122: CSV::Reader.parse(str_or_readable, fs, rs) do |row| 123: yield(row) 124: end 125: nil 126: else 127: CSV::Reader.create(str_or_readable, fs, rs).collect { |row| row } 128: end 129: end
Parse a line from given string. Bear in mind it parses ONE LINE. Rest of the string is ignored for example "a,b\r\nc,d" => [‘a’, ‘b’] and the second line ‘c,d’ is ignored.
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.
# File lib/csv.rb, line 137 137: def CSV.parse_line(src, fs = nil, rs = nil) 138: fs ||= ',' 139: if fs.is_a?(Fixnum) 140: fs = fs.chr 141: end 142: if !rs.nil? and rs.is_a?(Fixnum) 143: rs = rs.chr 144: end 145: idx = 0 146: res_type = :DT_COLSEP 147: row = [] 148: begin 149: while res_type == :DT_COLSEP 150: res_type, idx, cell = parse_body(src, idx, fs, rs) 151: row << cell 152: end 153: rescue IllegalFormatError 154: return [] 155: end 156: row 157: end
Parse a line from string. Consider using CSV.parse_line instead. To parse lines in CSV string, see EXAMPLE below.
EXAMPLE
src = "a,b\r\nc,d\r\ne,f" idx = 0 begin parsed = [] parsed_cells, idx = CSV.parse_row(src, idx, parsed) puts "Parsed #{ parsed_cells } cells." p parsed end while parsed_cells > 0
ARGS
src: a CSV data to be parsed. Must respond '[](idx)'. src[](idx) must return a char. (Not a string such as 'a', but 97). src[](idx_out_of_bounds) must return nil. A String satisfies this requirement. idx: index of parsing location of 'src'. 0 origin. out_dev: buffer for parsed cells. Must respond '<<(aString)'. col_sep: Column separator. ?, by default. If you want to separate fields with semicolon, give ?; here. row_sep: Row separator. nil by default. nil means "\r\n or \n". If you want to separate records with \r, give ?\r here.
RETURNS
parsed_cells: num of parsed cells. idx: index of next parsing location of 'src'.
# File lib/csv.rb, line 214 214: def CSV.parse_row(src, idx, out_dev, fs = nil, rs = nil) 215: fs ||= ',' 216: if fs.is_a?(Fixnum) 217: fs = fs.chr 218: end 219: if !rs.nil? and rs.is_a?(Fixnum) 220: rs = rs.chr 221: end 222: idx_backup = idx 223: parsed_cells = 0 224: res_type = :DT_COLSEP 225: begin 226: while res_type != :DT_ROWSEP 227: res_type, idx, cell = parse_body(src, idx, fs, rs) 228: if res_type == :DT_EOS 229: if idx == idx_backup #((parsed_cells == 0) and cell.nil?) 230: return 0, 0 231: end 232: res_type = :DT_ROWSEP 233: end 234: parsed_cells += 1 235: out_dev << cell 236: end 237: rescue IllegalFormatError 238: return 0, 0 239: end 240: return parsed_cells, idx 241: end
# File lib/csv.rb, line 97 97: def CSV.read(path, length = nil, offset = nil) 98: CSV.parse(IO.read(path, length, offset)) 99: end
# File lib/csv.rb, line 101 101: def CSV.readlines(path, rs = nil) 102: reader = open_reader(path, 'r', ',', rs) 103: begin 104: reader.collect { |row| row } 105: ensure 106: reader.close 107: end 108: end
# File lib/csv.rb, line 484 484: def generate_body(cell, out_dev, fs, rs) 485: if cell.nil? 486: # empty 487: else 488: cell = cell.to_s 489: row_data = cell.dup 490: if (row_data.gsub!('"', '""') or 491: row_data.index(fs) or 492: (rs and row_data.index(rs)) or 493: (/[\r\n]/ =~ row_data) or 494: (cell.empty?)) 495: out_dev << '"' << row_data << '"' 496: else 497: out_dev << row_data 498: end 499: end 500: end
# File lib/csv.rb, line 502 502: def generate_separator(type, out_dev, fs, rs) 503: case type 504: when :DT_COLSEP 505: out_dev << fs 506: when :DT_ROWSEP 507: out_dev << (rs || "\n") 508: end 509: end
# File lib/csv.rb, line 307 307: def open_reader(path, mode, fs, rs, &block) 308: file = File.open(path, mode) 309: if block 310: begin 311: CSV::Reader.parse(file, fs, rs) do |row| 312: yield(row) 313: end 314: ensure 315: file.close 316: end 317: nil 318: else 319: reader = CSV::Reader.create(file, fs, rs) 320: reader.close_on_terminate 321: reader 322: end 323: end
# File lib/csv.rb, line 325 325: def open_writer(path, mode, fs, rs, &block) 326: file = File.open(path, mode) 327: if block 328: begin 329: CSV::Writer.generate(file, fs, rs) do |writer| 330: yield(writer) 331: end 332: ensure 333: file.close 334: end 335: nil 336: else 337: writer = CSV::Writer.create(file, fs, rs) 338: writer.close_on_terminate 339: writer 340: end 341: end
# File lib/csv.rb, line 343 343: def parse_body(src, idx, fs, rs) 344: fs_str = fs 345: fs_size = fs_str.size 346: rs_str = rs || "\n" 347: rs_size = rs_str.size 348: fs_idx = rs_idx = 0 349: cell = Cell.new 350: state = :ST_START 351: quoted = cr = false 352: c = nil 353: last_idx = idx 354: while c = src[idx] 355: unless quoted 356: fschar = (c == fs_str[fs_idx]) 357: rschar = (c == rs_str[rs_idx]) 358: # simple 1 char backtrack 359: if !fschar and c == fs_str[0] 360: fs_idx = 0 361: fschar = true 362: if state == :ST_START 363: state = :ST_DATA 364: elsif state == :ST_QUOTE 365: raise IllegalFormatError 366: end 367: end 368: if !rschar and c == rs_str[0] 369: rs_idx = 0 370: rschar = true 371: if state == :ST_START 372: state = :ST_DATA 373: elsif state == :ST_QUOTE 374: raise IllegalFormatError 375: end 376: end 377: end 378: if c == ?" 379: fs_idx = rs_idx = 0 380: if cr 381: raise IllegalFormatError 382: end 383: cell << src[last_idx, (idx - last_idx)] 384: last_idx = idx 385: if state == :ST_DATA 386: if quoted 387: last_idx += 1 388: quoted = false 389: state = :ST_QUOTE 390: else 391: raise IllegalFormatError 392: end 393: elsif state == :ST_QUOTE 394: cell << c.chr 395: last_idx += 1 396: quoted = true 397: state = :ST_DATA 398: else # :ST_START 399: quoted = true 400: last_idx += 1 401: state = :ST_DATA 402: end 403: elsif fschar or rschar 404: if fschar 405: fs_idx += 1 406: end 407: if rschar 408: rs_idx += 1 409: end 410: sep = nil 411: if fs_idx == fs_size 412: if state == :ST_START and rs_idx > 0 and fs_idx < rs_idx 413: state = :ST_DATA 414: end 415: cell << src[last_idx, (idx - last_idx - (fs_size - 1))] 416: last_idx = idx 417: fs_idx = rs_idx = 0 418: if cr 419: raise IllegalFormatError 420: end 421: sep = :DT_COLSEP 422: elsif rs_idx == rs_size 423: if state == :ST_START and fs_idx > 0 and rs_idx < fs_idx 424: state = :ST_DATA 425: end 426: if !(rs.nil? and cr) 427: cell << src[last_idx, (idx - last_idx - (rs_size - 1))] 428: last_idx = idx 429: end 430: fs_idx = rs_idx = 0 431: sep = :DT_ROWSEP 432: end 433: if sep 434: if state == :ST_DATA 435: return sep, idx + 1, cell; 436: elsif state == :ST_QUOTE 437: return sep, idx + 1, cell; 438: else # :ST_START 439: return sep, idx + 1, nil 440: end 441: end 442: elsif rs.nil? and c == ?\r 443: # special \r treatment for backward compatibility 444: fs_idx = rs_idx = 0 445: if cr 446: raise IllegalFormatError 447: end 448: cell << src[last_idx, (idx - last_idx)] 449: last_idx = idx 450: if quoted 451: state = :ST_DATA 452: else 453: cr = true 454: end 455: else 456: fs_idx = rs_idx = 0 457: if state == :ST_DATA or state == :ST_START 458: if cr 459: raise IllegalFormatError 460: end 461: state = :ST_DATA 462: else # :ST_QUOTE 463: raise IllegalFormatError 464: end 465: end 466: idx += 1 467: end 468: if state == :ST_START 469: if fs_idx > 0 or rs_idx > 0 470: state = :ST_DATA 471: else 472: return :DT_EOS, idx, nil 473: end 474: elsif quoted 475: raise IllegalFormatError 476: elsif cr 477: raise IllegalFormatError 478: end 479: cell << src[last_idx, (idx - last_idx)] 480: last_idx = idx 481: return :DT_EOS, idx, cell 482: end