| 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