carray-io-csv 0.9.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  - - ">="