carray-io-csv 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9cb40651d13b603f919dced66f36937c7b116381be98f4d2e27ce9bf69608c4
4
- data.tar.gz: 8b5c8cc847d24dc3ba46b1969da7f2ede0fdcc18646577db32883a7a8b70b9e1
3
+ metadata.gz: 3f4aad4e63e332f57709cd3b0c13791ae93fa43e39cc6627729302be5f255f89
4
+ data.tar.gz: cd0c329a6612e7ebca7c3b831ffbba6b3525cbe91f95e06e467d70604d8662e9
5
5
  SHA512:
6
- metadata.gz: f3bfe3db49f4c1ce530bf5a640d9ba4d6fd3f2673ee813fa3b2de778de86fbf9ec1618e3a5a2fbd0883b981159a92329f0ea9ec9b81df938b606c36199aa6462
7
- data.tar.gz: 6ccb47ecd46ecacac1c13dba205206527d2ac4692226c1659cc9f1affe60bd8f6a45e1c6899b960a1d1f0b7a2591a3fc4933193b29f77376b6ffa8ed62939da5
6
+ metadata.gz: 72874fc22721a412621f0a47f31f74240b40b79f87b419a2f74c84c40754262f9ea816884b96b26ecd233efe20e40d584064dcd0eb01fe6d4b2b18210e3b1844
7
+ data.tar.gz: fc04ddc1a7a05aca96a1afb92480c09fb00c74f0deb6723476edd9abe43576eebdfc98c5ea7dfb726f297c8e2232c4725185301f2412d0af9025a3af731969b8
data/README.md ADDED
File without changes
@@ -1,5 +1,5 @@
1
1
  Gem::Specification::new do |s|
2
- version = "0.9.1"
2
+ version = "1.0.0"
3
3
  files = Dir.glob("**/*") - [
4
4
  Dir.glob("carray-io-csv-*.gem"),
5
5
  Dir.glob("test/**/*"),
@@ -18,10 +18,8 @@ Gem::Specification::new do |s|
18
18
  s.email = ""
19
19
  s.homepage = 'https://github.com/himotoyoshi/carray-io-csv'
20
20
  s.files = files
21
- # s.extensions = [ "ext/extconf.rb" ]
22
- s.required_ruby_version = ">= 1.8.1"
23
- s.add_runtime_dependency 'carray', '~> 1.3'
24
- s.add_runtime_dependency 'rcsv', '~> 0.3.1'
21
+ s.required_ruby_version = ">= 2.4.0"
22
+ s.add_runtime_dependency 'carray', '~> 1.5'
25
23
 
26
24
  end
27
25
 
@@ -0,0 +1,593 @@
1
+ # ----------------------------------------------------------------------------
2
+ #
3
+ # carray/io/csv.rb
4
+ #
5
+ # This file is part of Ruby/CArray extension library.
6
+ # You can redistribute it and/or modify it under the terms of
7
+ # the Ruby Licence.
8
+ #
9
+ # Copyright (C) 2005 Hiroki Motoyoshi
10
+ #
11
+ # ----------------------------------------------------------------------------
12
+ #
13
+ # CSV data reader/writer for CArray (in DSL approach)
14
+ #
15
+ # For reading (explicit form)
16
+ #
17
+ # csv = CA::CSVReader.new {
18
+ # header # read a line as "names" header
19
+ # header :units # read a line as "units" header
20
+ # skip 1 # skip a line
21
+ # body 10 # read values
22
+ # process { |name, column| # post processing
23
+ # case name # name : name of columns (string|integer)
24
+ # when "Date" # column : column carray
25
+ # column.map!{|x| Date.parse(x) }
26
+ # else
27
+ # column[] = column.double
28
+ # end
29
+ # }
30
+ # }
31
+ #
32
+ # data1 = csv.read_file(file1)
33
+ # data2 = csv.read_file(file2)
34
+ # data3 = csv.read_file(file3)
35
+ #
36
+ # For reading (implicit form)
37
+ #
38
+ # data = CArray.load_csv(file) { ... definitions ... }
39
+ # data = CArray.from_csv(io|string) { ... definitions ... }
40
+ #
41
+ # For writing (explict form)
42
+ #
43
+ # csv = CA::CSVWriter.new {
44
+ # names ["Date", "a", "b", "c"] # set name of columns
45
+ # process { |name, column| # pre processing
46
+ # case name # name : name of column
47
+ # when "Date" # column : column carray
48
+ # column.map!{|x| x.to_s }
49
+ # else
50
+ # column.map!{|x| "%.2f" % x }
51
+ # end
52
+ # }
53
+ # puts "sample CSV data" # print any string
54
+ # header # write names in header
55
+ # header ["","mm","cm","m"] # write units in header
56
+ # write # write values
57
+ # }
58
+ #
59
+ # data1.write_file(file1)
60
+ # data2.write_file(file2)
61
+ # data3.write_file(file3)
62
+ #
63
+ # For writing (implicit form)
64
+ #
65
+ # data.save_csv(file) { ... definitions ... }
66
+ # data.to_csv([io|string]) { ... definitions ... }
67
+ #
68
+
69
+ require "stringio"
70
+ require "rcsv"
71
+ require "strscan"
72
+
73
+ module CA
74
+
75
+ class CSVReader
76
+
77
+ def initialize (sep: ",", rs: $/, &block)
78
+ @sep = sep
79
+ @rs = rs
80
+ @block = block
81
+ end
82
+
83
+ def read_io (io)
84
+ return Processor.new(io, sep: @sep, rs: @rs, &@block).run
85
+ end
86
+
87
+ def read_string (string)
88
+ return read_io(StringIO.new(string))
89
+ end
90
+
91
+ def read_file (filename, encoding: nil)
92
+ File.open(filename, encoding: encoding) { |io|
93
+ return read_io(io)
94
+ }
95
+ end
96
+
97
+ class Processor
98
+
99
+ def initialize (io, sep:, rs:, &block)
100
+ @io = io
101
+ @sep = sep
102
+ @rs = rs
103
+ @block = block || proc { body }
104
+ @namelist = nil
105
+ @names = nil
106
+ @headerlist = []
107
+ @header = {}
108
+ @note = ""
109
+ @table = nil
110
+ @regexp_simple1 = /#{@sep}/
111
+ @regexp_simple2 = / *#{@sep} */
112
+ @regexp_pat1 = /\A([^"#{@sep}][^#{@sep}]*) *#{@sep} */
113
+ @regexp_pat2 = /\A"([^"]+)" *#{@sep} */
114
+ @regexp_pat3 = /\A"((?:[^"]+|"")+)" *#{@sep} */
115
+ @regexp_pat4 = /\A(?:""|) *#{@sep} */
116
+ @sc = StringScanner.new("")
117
+ end
118
+
119
+ def run
120
+ case @block.arity
121
+ when 1
122
+ @block.call(self)
123
+ when -1, 0
124
+ instance_exec(&@block)
125
+ else
126
+ raise "invalid block paramter"
127
+ end
128
+ if @header.has_key?("names")
129
+ @header["names"].each_with_index do |name, k|
130
+ if name.nil? or name.empty?
131
+ @header["names"][k] = "c#{k}"
132
+ end
133
+ end
134
+ else
135
+ @header["names"] = (0...@cols).map{|k| "c#{k}"}
136
+ end
137
+ header = @header
138
+ note = @note
139
+ @table.instance_exec{
140
+ @names = header["names"]
141
+ @header = header
142
+ @note = note
143
+ }
144
+ @table.extend(CArray::TableMethods)
145
+ @table.column_names = header["names"]
146
+ class << @table
147
+ attr_reader :note
148
+ def header (name=nil)
149
+ if name
150
+ return @header[name.to_s]
151
+ else
152
+ return @header
153
+ end
154
+ end
155
+ end
156
+ return @table
157
+ end
158
+
159
+ def column_names (*namelist)
160
+ if @header.has_key?("names")
161
+ warn "override header['names']"
162
+ end
163
+ @names = namelist.map(&:to_s)
164
+ @header["names"] = @names
165
+ return namelist
166
+ end
167
+
168
+ alias columns column_names
169
+
170
+ def header (name = "names")
171
+ name = name.to_s
172
+ list = csv_feed()
173
+ if name == "names"
174
+ if @names
175
+ raise "already 'names' defined"
176
+ end
177
+ @names = list
178
+ end
179
+ @header[name] = list
180
+ @headerlist.push(name)
181
+ return list
182
+ end
183
+
184
+ attr_reader :names
185
+
186
+ def note (n=1)
187
+ list = []
188
+ n.times { list << @io.gets(@rs) }
189
+ @note << (text = list.join)
190
+ return text
191
+ end
192
+
193
+ def skip (n=1)
194
+ n.times { @io.gets(@rs) }
195
+ end
196
+
197
+ def body (n=nil, cols=nil)
198
+ data = []
199
+ count = 0
200
+ if cols
201
+ @cols = cols
202
+ elsif @names
203
+ @cols = @names.size
204
+ else
205
+ list = csv_feed()
206
+ if list.nil?
207
+ @rows = 0
208
+ @table = CArray.object(@rows, @cols)
209
+ return
210
+ end
211
+ data.push(list)
212
+ count += 1
213
+ @cols = list.size
214
+ end
215
+ if n
216
+ lsize = nil
217
+ while count < n and list = csv_feed(@cols)
218
+ lsize = list.size
219
+ if lsize == @cols
220
+ data.push(list)
221
+ elsif lsize <= @cols
222
+ record = Array.new(@cols, nil)
223
+ record[0,lsize] = list
224
+ data.push(record)
225
+ else
226
+ extra = Array.new(lsize - @cols, nil)
227
+ data.each do |row|
228
+ row.push(*extra)
229
+ end
230
+ data.push(list)
231
+ @cols = lsize
232
+ # raise "csv parse error : too large column number at line #{@io.lineno}"
233
+ end
234
+ count += 1
235
+ end
236
+ else
237
+ unless @io.eof?
238
+ data += Rcsv.parse(@io, column_separator: @sep, header: :none)
239
+ end
240
+ end
241
+ @rows = data.size
242
+ @table = CArray.object(@rows, @cols){ data }
243
+ @table[:eq,""] = nil
244
+ end
245
+
246
+ def rename (name, newname)
247
+ names = @header["names"]
248
+ i = names.index(name)
249
+ names[i] = newname
250
+ @names = @header["names"]
251
+ end
252
+
253
+ def downcase
254
+ @header["names"] = @header["names"].map(&:downcase)
255
+ @names = @header["names"]
256
+ end
257
+
258
+ def select (*namelist)
259
+ @namelist = namelist.empty? ? nil : namelist
260
+ case @namelist
261
+ when nil
262
+ when Array
263
+ index = (0...@cols).map.to_a
264
+ index_list = @namelist.map{ |x|
265
+ case x
266
+ when Integer
267
+ x
268
+ when Range
269
+ index[x]
270
+ when String, Symbol
271
+ if @names and i = @names.index(x.to_s)
272
+ i
273
+ else
274
+ raise "invalid argument #{x}"
275
+ end
276
+ else
277
+ raise "invalid argument"
278
+ end
279
+ }.flatten
280
+ @table = @table[nil, CA_INT(index_list)].to_ca
281
+ @header.keys.each do |k|
282
+ @header[k] = @header[k].values_at(*index_list)
283
+ end
284
+ @names = @header["names"]
285
+ else
286
+ raise
287
+ end
288
+ end
289
+
290
+ def process
291
+ if @namelist
292
+ @namelist.each_with_index do |name, i|
293
+ yield(name, @table[nil, i])
294
+ end
295
+ elsif @names
296
+ @names.each_with_index do |name, i|
297
+ yield(name, @table[nil, i])
298
+ end
299
+ else
300
+ @table.dim1.times do |i|
301
+ yield(i, @table[nil,i])
302
+ end
303
+ end
304
+ end
305
+
306
+ def convert (data_type, options={}, &block)
307
+ if block_given?
308
+ if data_type.is_a?(Class) and data_type < CA::Struct
309
+ @table = @table[:i, nil].convert(data_type, &block)
310
+ else
311
+ @table = @table.convert(data_type, options, &block)
312
+ end
313
+ else
314
+ if data_type.is_a?(Class) and data_type < CA::Struct
315
+ @table = @table[:i,nil].convert(data_type) { |b|
316
+ data_type.new(*b[0,nil])
317
+ }
318
+ else
319
+ @table = @table.to_type(data_type, options)
320
+ end
321
+ end
322
+ end
323
+
324
+ private
325
+
326
+ def csv_feed (cols=nil)
327
+ if @io.eof?
328
+ return nil
329
+ end
330
+ line = nil
331
+ loop do
332
+ if newline = @io.gets(@rs)
333
+ if line
334
+ line << newline
335
+ else
336
+ line = newline
337
+ end
338
+ count_quote = line.count('"')
339
+ else
340
+ line = ""
341
+ count_quote = 0
342
+ end
343
+ if count_quote == 0
344
+ line.chomp!
345
+ if line.count(' ') == 0
346
+ return line.split(@sep, -1) ### /#{@sep}/
347
+ else
348
+ return line.split(@regexp_simple2, -1) ### / *#{@sep} */
349
+ end
350
+ end
351
+ if count_quote % 2 == 0
352
+ line.chomp!
353
+ return csv_split(line, cols)
354
+ else
355
+ if newline
356
+ next
357
+ else
358
+ raise "csv parse error"
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ def csv_split (text, cols=nil)
365
+ if cols
366
+ csv = Array.new(cols)
367
+ else
368
+ csv = []
369
+ end
370
+ text << @sep
371
+ @sc.string = text
372
+ i = 0
373
+ begin
374
+ case
375
+ when @sc.scan(@regexp_pat1)
376
+ ### /\A([^"#{@sep}][^#{@sep}]*) *#{@sep} */
377
+ csv[i] = @sc[1]
378
+ when @sc.scan(@regexp_pat2)
379
+ ### /\A"([^"]+)" *#{@sep} */
380
+ csv[i] = @sc[1]
381
+ when @sc.scan(@regexp_pat3)
382
+ ### /\A"((?:[^"]+|"")+)" *#{@sep} */
383
+ s = @sc[1]
384
+ if s =~ /"/
385
+ csv[i] = s.gsub(/""/, '"')
386
+ else
387
+ csv[i] = s
388
+ end
389
+ when @sc.scan(@regexp_pat4)
390
+ ### /\A(?:""|) *#{@sep} */
391
+ csv[i] = nil
392
+ else
393
+ raise "csv parse error"
394
+ end
395
+ i += 1
396
+ end until @sc.eos?
397
+ return csv
398
+ end
399
+
400
+ end
401
+
402
+ end
403
+
404
+ end
405
+
406
+ module CA
407
+
408
+ class CSVWriter # :nodoc:
409
+
410
+ def initialize (sep=",", rs=$/, fill="", &block)
411
+ @block = block
412
+ @sep = sep
413
+ @rs = rs
414
+ @fill = fill
415
+ end
416
+
417
+ def write_io (table, io)
418
+ return Processor.new(table, io, @sep, @rs, @fill, &@block).run
419
+ end
420
+
421
+ def write_string (table, string)
422
+ write_io(table, StringIO.new(string))
423
+ return string
424
+ end
425
+
426
+ def write_file (table, filename, mode="w")
427
+ open(filename, mode) { |io|
428
+ return write_io(table, io)
429
+ }
430
+ end
431
+
432
+ class Processor # :nodoc:
433
+
434
+ def initialize (table, io, sep, rs, fill, &block)
435
+ @io = io
436
+ @sep = sep
437
+ @rs = rs
438
+ @fill = fill
439
+ @block = block || proc { body }
440
+ if table.has_data_class?
441
+ @names = table.members
442
+ @table = CArray.merge(CA_OBJECT, table[nil].fields)
443
+ else
444
+ @names = table.instance_exec{ @names }
445
+ if @names.nil?
446
+ @names = table.instance_exec{ @column_names }
447
+ end
448
+ case
449
+ when table.rank > 2
450
+ @table = table.reshape(false,nil).object
451
+ when table.rank == 1
452
+ @table = table[:%,1].object ### convert to CA_OBJECT
453
+ else
454
+ @table = table.object ### convert to CA_OBJECT
455
+ end
456
+ end
457
+ if @table.has_mask?
458
+ @table.unmask(@fill)
459
+ end
460
+ @regexp_simple = /#{@sep}/o
461
+ end
462
+
463
+ def csv_quote (text)
464
+ text = text.dup
465
+ if text.gsub!(/"/, '""') or text =~ @regexp_simple ### /#{@sep}|"/
466
+ text = '"' + text + '"'
467
+ end
468
+ return text
469
+ end
470
+
471
+ def run
472
+ case @block.arity
473
+ when 1
474
+ @block.call(self)
475
+ when -1, 0
476
+ instance_exec(&@block)
477
+ else
478
+ raise "invalid block parameter"
479
+ end
480
+ end
481
+
482
+ # set @names
483
+ def names (list)
484
+ @names = list
485
+ end
486
+
487
+ # puts header
488
+ def header (list = @names)
489
+ @io.write list.map{|s| csv_quote(s)}.join(@sep)
490
+ @io.write(@rs)
491
+ end
492
+
493
+ # puts any strings
494
+ def puts (*argv)
495
+ @io.print(*argv)
496
+ @io.write(@rs)
497
+ end
498
+
499
+ # write value
500
+ # If option :strict is set, do csv_quote for string element
501
+ def body (strict: true, format: nil)
502
+ if strict
503
+ case @table.data_type
504
+ when CA_OBJECT
505
+ table = @table.to_ca
506
+ table[:is_kind_of, String].map! { |s| csv_quote(s) }
507
+ when CA_FIXLEN
508
+ table = @table.object
509
+ table.map! { |s| csv_quote(s) }
510
+ else
511
+ table = @table.object
512
+ end
513
+ else
514
+ table = @table
515
+ end
516
+ if format
517
+ table.dim0.times do |i|
518
+ @io.write Kernel::format(format,*table[i,nil].to_a)
519
+ @io.write(@rs)
520
+ end
521
+ else
522
+ table.dim0.times do |i|
523
+ @io.write table[i,nil].to_a.join(@sep)
524
+ @io.write(@rs)
525
+ end
526
+ end
527
+ end
528
+
529
+ # pre processing data
530
+ def process (namelist = @names)
531
+ if namelist
532
+ namelist.each_with_index do |name, i|
533
+ yield(name, @table[nil, i])
534
+ end
535
+ else
536
+ @table.dim1.times do |i|
537
+ yield(i, @table[nil,i])
538
+ end
539
+ end
540
+ end
541
+ end
542
+ end
543
+ end
544
+
545
+ class CArray
546
+
547
+ def self.load_csv (file, sep: ",", rs: $/, encoding: nil, &block)
548
+ reader = CA::CSVReader.new(sep: sep, rs: rs, &block)
549
+ return reader.read_file(file, encoding: encoding)
550
+ end
551
+
552
+ def self.from_csv (io, sep: ",", rs: $/, &block)
553
+ reader = CA::CSVReader.new(sep: sep, rs: rs, &block)
554
+ case io
555
+ when IO, StringIO
556
+ return reader.read_io(io)
557
+ when String
558
+ return reader.read_string(io)
559
+ else
560
+ raise "invalid argument"
561
+ end
562
+ end
563
+
564
+ def save_csv (file, rs: $/, sep: ",", fill: "", mode: "w", &block)
565
+ writer = CA::CSVWriter.new(sep, rs, fill, &block)
566
+ return writer.write_file(self, file, mode)
567
+ end
568
+
569
+ def to_csv (io="", rs: $/, sep: ",", fill: "", &block)
570
+ writer = CA::CSVWriter.new(sep, rs, fill, &block)
571
+ case io
572
+ when IO, StringIO
573
+ return writer.write_io(self, io)
574
+ when String
575
+ return writer.write_string(self, io)
576
+ end
577
+ end
578
+
579
+ def to_tabular (**option)
580
+ option = {:sep=>" ", :names=>nil}.update(option)
581
+ if option[:names]
582
+ names = option[:names]
583
+ elsif self.respond_to?(:names)
584
+ names = self.names
585
+ end
586
+ sep = option[:sep]
587
+ data = self.to_ca.map! {|s| s.to_s }
588
+ table = CArray.join([names.to_ca], [data])
589
+ length = table.convert{|s| s.length}.max(0)
590
+ table.map_with_index! {|s, idx| s.rjust(length[idx[1]]) }.to_csv.gsub(/,/,sep)
591
+ end
592
+ end
593
+
@@ -67,21 +67,21 @@
67
67
  #
68
68
 
69
69
  require "stringio"
70
- require "rcsv"
71
70
  require "strscan"
72
71
 
73
72
  module CA
74
73
 
75
- class CSVReader
76
-
77
- def initialize (sep: ",", rs: $/, &block)
78
- @sep = sep
79
- @rs = rs
74
+ class CSVReader # :nodoc:
75
+
76
+ def initialize (sep: ",", rs: $/, quote_char: '"', &block)
80
77
  @block = block
78
+ @sep = sep
79
+ @rs = rs
80
+ @quote_char = quote_char
81
81
  end
82
82
 
83
83
  def read_io (io)
84
- return Processor.new(io, sep: @sep, rs: @rs, &@block).run
84
+ return Processor.new(io, @sep, @rs, @quote_char, &@block).run
85
85
  end
86
86
 
87
87
  def read_string (string)
@@ -93,13 +93,15 @@ module CA
93
93
  return read_io(io)
94
94
  }
95
95
  end
96
-
97
- class Processor
98
-
99
- def initialize (io, sep:, rs:, &block)
96
+
97
+ class Processor # :nodoc:
98
+
99
+ def initialize (io, sep, rs=$/, quote_char='"', &block)
100
100
  @io = io
101
101
  @sep = sep
102
102
  @rs = rs
103
+ @quote_char = quote_char
104
+ @sc = StringScanner.new("")
103
105
  @block = block || proc { body }
104
106
  @namelist = nil
105
107
  @names = nil
@@ -109,14 +111,15 @@ module CA
109
111
  @table = nil
110
112
  @regexp_simple1 = /#{@sep}/
111
113
  @regexp_simple2 = / *#{@sep} */
112
- @regexp_pat1 = /\A([^"#{@sep}][^#{@sep}]*) *#{@sep} */
113
- @regexp_pat2 = /\A"([^"]+)" *#{@sep} */
114
- @regexp_pat3 = /\A"((?:[^"]+|"")+)" *#{@sep} */
115
- @regexp_pat4 = /\A(?:""|) *#{@sep} */
116
- @sc = StringScanner.new("")
114
+ @regexp_pat1 = /\A([^#{@quote_char}#{@sep}][^#{@sep}]*) *#{@sep} */
115
+ @regexp_pat2 = /\A#{@quote_char}([^#{@quote_char}]+)#{@quote_char} *#{@sep} */
116
+ @regexp_pat3 = /\A#{@quote_char}((?:[^#{@quote_char}]+|#{@quote_char}#{@quote_char})+)#{@quote_char} *#{@sep} */
117
+ @regexp_pat4 = /\A(?:#{@quote_char}#{@quote_char}|) *#{@sep} */
118
+ @regexp_pat5 = /#{@quote_char}/
119
+ @regexp_pat6 = /#{@quote_char}#{@quote_char}/
117
120
  end
118
121
 
119
- def run
122
+ def run
120
123
  case @block.arity
121
124
  when 1
122
125
  @block.call(self)
@@ -125,15 +128,6 @@ module CA
125
128
  else
126
129
  raise "invalid block paramter"
127
130
  end
128
- if @header.has_key?("names")
129
- @header["names"].each_with_index do |name, k|
130
- if name.nil? or name.empty?
131
- @header["names"][k] = "c#{k}"
132
- end
133
- end
134
- else
135
- @header["names"] = (0...@cols).map{|k| "c#{k}"}
136
- end
137
131
  header = @header
138
132
  note = @note
139
133
  @table.instance_exec{
@@ -165,8 +159,6 @@ module CA
165
159
  return namelist
166
160
  end
167
161
 
168
- alias columns column_names
169
-
170
162
  def header (name = "names")
171
163
  name = name.to_s
172
164
  list = csv_feed()
@@ -180,7 +172,7 @@ module CA
180
172
  @headerlist.push(name)
181
173
  return list
182
174
  end
183
-
175
+
184
176
  attr_reader :names
185
177
 
186
178
  def note (n=1)
@@ -195,8 +187,12 @@ module CA
195
187
  end
196
188
 
197
189
  def body (n=nil, cols=nil)
190
+ unless n
191
+ n = 0x80000000
192
+ end
198
193
  data = []
199
194
  count = 0
195
+
200
196
  if cols
201
197
  @cols = cols
202
198
  elsif @names
@@ -212,49 +208,33 @@ module CA
212
208
  count += 1
213
209
  @cols = list.size
214
210
  end
215
- if n
216
- lsize = nil
217
- while count < n and list = csv_feed(@cols)
218
- lsize = list.size
219
- if lsize == @cols
220
- data.push(list)
221
- elsif lsize <= @cols
222
- record = Array.new(@cols, nil)
223
- record[0,lsize] = list
224
- data.push(record)
225
- else
226
- extra = Array.new(lsize - @cols, nil)
227
- data.each do |row|
228
- row.push(*extra)
229
- end
230
- data.push(list)
231
- @cols = lsize
232
- # raise "csv parse error : too large column number at line #{@io.lineno}"
211
+
212
+ lsize = nil
213
+ while count < n and list = csv_feed(@cols)
214
+ lsize = list.size
215
+ if lsize == @cols
216
+ data.push(list)
217
+ elsif lsize < @cols
218
+ record = Array.new(@cols, nil)
219
+ record[0,lsize] = list
220
+ data.push(record)
221
+ else
222
+ extra = Array.new(lsize - @cols, nil)
223
+ data.each do |row|
224
+ row.push(*extra)
233
225
  end
234
- count += 1
235
- end
236
- else
237
- unless @io.eof?
238
- data += Rcsv.parse(@io, column_separator: @sep, header: :none)
226
+ data.push(list)
227
+ @cols = lsize
228
+ # raise "csv parse error : too large column number at line #{@io.lineno}"
239
229
  end
230
+ count += 1
240
231
  end
232
+
241
233
  @rows = data.size
242
234
  @table = CArray.object(@rows, @cols){ data }
243
235
  @table[:eq,""] = nil
244
236
  end
245
237
 
246
- def rename (name, newname)
247
- names = @header["names"]
248
- i = names.index(name)
249
- names[i] = newname
250
- @names = @header["names"]
251
- end
252
-
253
- def downcase
254
- @header["names"] = @header["names"].map(&:downcase)
255
- @names = @header["names"]
256
- end
257
-
258
238
  def select (*namelist)
259
239
  @namelist = namelist.empty? ? nil : namelist
260
240
  case @namelist
@@ -281,7 +261,6 @@ module CA
281
261
  @header.keys.each do |k|
282
262
  @header[k] = @header[k].values_at(*index_list)
283
263
  end
284
- @names = @header["names"]
285
264
  else
286
265
  raise
287
266
  end
@@ -335,7 +314,7 @@ module CA
335
314
  else
336
315
  line = newline
337
316
  end
338
- count_quote = line.count('"')
317
+ count_quote = line.count(@quote_char)
339
318
  else
340
319
  line = ""
341
320
  count_quote = 0
@@ -381,8 +360,9 @@ module CA
381
360
  when @sc.scan(@regexp_pat3)
382
361
  ### /\A"((?:[^"]+|"")+)" *#{@sep} */
383
362
  s = @sc[1]
384
- if s =~ /"/
385
- csv[i] = s.gsub(/""/, '"')
363
+ if s =~ @regexp_pat5 ### /"/
364
+ csv[i] = s.gsub(@regexp_pat6, @quote_char)
365
+ ### /""/
386
366
  else
387
367
  csv[i] = s
388
368
  end
@@ -398,24 +378,21 @@ module CA
398
378
  end
399
379
 
400
380
  end
401
-
402
381
  end
403
-
404
382
  end
405
383
 
406
384
  module CA
407
385
 
408
386
  class CSVWriter # :nodoc:
409
387
 
410
- def initialize (sep=",", rs=$/, fill="", &block)
388
+ def initialize (sep=",", rs=$/, fill=nil, &block) ### FIXME: fill is not used
411
389
  @block = block
412
390
  @sep = sep
413
391
  @rs = rs
414
- @fill = fill
415
392
  end
416
393
 
417
394
  def write_io (table, io)
418
- return Processor.new(table, io, @sep, @rs, @fill, &@block).run
395
+ return Processor.new(table, io, @sep, @rs, &@block).run
419
396
  end
420
397
 
421
398
  def write_string (table, string)
@@ -431,19 +408,19 @@ module CA
431
408
 
432
409
  class Processor # :nodoc:
433
410
 
434
- def initialize (table, io, sep, rs, fill, &block)
411
+ def initialize (table, io, sep, rs, &block)
435
412
  @io = io
436
413
  @sep = sep
437
414
  @rs = rs
438
- @fill = fill
439
415
  @block = block || proc { body }
440
416
  if table.has_data_class?
441
417
  @names = table.members
442
418
  @table = CArray.merge(CA_OBJECT, table[nil].fields)
443
419
  else
444
- @names = table.instance_exec{ @names }
445
- if @names.nil?
446
- @names = table.instance_exec{ @column_names }
420
+ if table.respond_to?(:column_names)
421
+ @names = table.column_names
422
+ else
423
+ @names = table.instance_exec{ @names }
447
424
  end
448
425
  case
449
426
  when table.rank > 2
@@ -454,9 +431,6 @@ module CA
454
431
  @table = table.object ### convert to CA_OBJECT
455
432
  end
456
433
  end
457
- if @table.has_mask?
458
- @table.unmask(@fill)
459
- end
460
434
  @regexp_simple = /#{@sep}/o
461
435
  end
462
436
 
@@ -485,7 +459,7 @@ module CA
485
459
  end
486
460
 
487
461
  # puts header
488
- def header (list = @names)
462
+ def header (list=@names)
489
463
  @io.write list.map{|s| csv_quote(s)}.join(@sep)
490
464
  @io.write(@rs)
491
465
  end
@@ -498,29 +472,25 @@ module CA
498
472
 
499
473
  # write value
500
474
  # If option :strict is set, do csv_quote for string element
501
- def body (strict: true, format: nil)
502
- if strict
475
+ def body (opt = {:strict=>true})
476
+ if opt[:strict]
503
477
  case @table.data_type
504
478
  when CA_OBJECT
505
- table = @table.to_ca
479
+ table = @table
506
480
  table[:is_kind_of, String].map! { |s| csv_quote(s) }
507
481
  when CA_FIXLEN
508
482
  table = @table.object
509
483
  table.map! { |s| csv_quote(s) }
510
484
  else
511
- table = @table.object
485
+ table = @table.object
512
486
  end
513
- else
514
- table = @table
515
- end
516
- if format
517
487
  table.dim0.times do |i|
518
- @io.write Kernel::format(format,*table[i,nil].to_a)
488
+ @io.write table[i,nil].to_a.join(@sep)
519
489
  @io.write(@rs)
520
- end
490
+ end
521
491
  else
522
- table.dim0.times do |i|
523
- @io.write table[i,nil].to_a.join(@sep)
492
+ @table.dim0.times do |i|
493
+ @io.write @table[i,nil].to_a.join(@sep)
524
494
  @io.write(@rs)
525
495
  end
526
496
  end
@@ -544,13 +514,32 @@ end
544
514
 
545
515
  class CArray
546
516
 
547
- def self.load_csv (file, sep: ",", rs: $/, encoding: nil, &block)
548
- reader = CA::CSVReader.new(sep: sep, rs: rs, &block)
517
+ def self.read_csv (file, sep: ",", rs: $/, quote_char: '"', encoding: nil, &block)
518
+ reader = CA::CSVReader.new(sep: sep, rs: rs, quote_char: quote_char, &block)
549
519
  return reader.read_file(file, encoding: encoding)
550
520
  end
521
+
522
+ def self.load_csv (file, sep: ",", rs: $/, quote_char: '"', encoding: nil, &block)
523
+ warn "CArray.load_csv will obsolete, use CArray.read_csv"
524
+ reader = CA::CSVReader.new(sep: sep, rs: rs, quote_char: quote_char, &block)
525
+ return reader.read_file(file, encoding: encoding)
526
+ end
527
+
528
+ def self.parse_csv (io, sep: ",", rs: $/, quote_char: '"', &block)
529
+ reader = CA::CSVReader.new(sep: sep, rs: rs, quote_char: quote_char, &block)
530
+ case io
531
+ when IO, StringIO
532
+ return reader.read_io(io)
533
+ when String
534
+ return reader.read_string(io)
535
+ else
536
+ raise "invalid argument"
537
+ end
538
+ end
551
539
 
552
- def self.from_csv (io, sep: ",", rs: $/, &block)
553
- reader = CA::CSVReader.new(sep: sep, rs: rs, &block)
540
+ def self.from_csv (io, sep: ",", rs: $/, quote_char: '"', &block)
541
+ warn "CArray.from_csv will obsolete, use CArray.parse_csv"
542
+ reader = CA::CSVReader.new(sep: sep, rs: rs, quote_char: quote_char, &block)
554
543
  case io
555
544
  when IO, StringIO
556
545
  return reader.read_io(io)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carray-io-csv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroki Motoyoshi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-09 00:00:00.000000000 Z
11
+ date: 2021-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: carray
@@ -16,37 +16,25 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '1.5'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
27
- - !ruby/object:Gem::Dependency
28
- name: rcsv
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 0.3.1
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 0.3.1
26
+ version: '1.5'
41
27
  description: " CSV interface for CArray\n"
42
28
  email: ''
43
29
  executables: []
44
30
  extensions: []
45
31
  extra_rdoc_files: []
46
32
  files:
33
+ - README.md
47
34
  - Rakefile
48
35
  - carray-io-csv.gemspec
49
36
  - lib/carray-io-csv.rb
37
+ - lib/carray-io-csv/core.bak.rb
50
38
  - lib/carray-io-csv/core.rb
51
39
  homepage: https://github.com/himotoyoshi/carray-io-csv
52
40
  licenses:
@@ -60,7 +48,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
48
  requirements:
61
49
  - - ">="
62
50
  - !ruby/object:Gem::Version
63
- version: 1.8.1
51
+ version: 2.4.0
64
52
  required_rubygems_version: !ruby/object:Gem::Requirement
65
53
  requirements:
66
54
  - - ">="