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 +4 -4
- data/README.md +0 -0
- data/carray-io-csv.gemspec +3 -5
- data/lib/carray-io-csv/core.bak.rb +593 -0
- data/lib/carray-io-csv/core.rb +87 -98
- metadata +7 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f4aad4e63e332f57709cd3b0c13791ae93fa43e39cc6627729302be5f255f89
|
4
|
+
data.tar.gz: cd0c329a6612e7ebca7c3b831ffbba6b3525cbe91f95e06e467d70604d8662e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72874fc22721a412621f0a47f31f74240b40b79f87b419a2f74c84c40754262f9ea816884b96b26ecd233efe20e40d584064dcd0eb01fe6d4b2b18210e3b1844
|
7
|
+
data.tar.gz: fc04ddc1a7a05aca96a1afb92480c09fb00c74f0deb6723476edd9abe43576eebdfc98c5ea7dfb726f297c8e2232c4725185301f2412d0af9025a3af731969b8
|
data/README.md
ADDED
File without changes
|
data/carray-io-csv.gemspec
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
Gem::Specification::new do |s|
|
2
|
-
version = "0.
|
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
|
-
|
22
|
-
s.
|
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
|
+
|
data/lib/carray-io-csv/core.rb
CHANGED
@@ -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,
|
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
|
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([
|
113
|
-
@regexp_pat2 = /\A
|
114
|
-
@regexp_pat3 = /\A
|
115
|
-
@regexp_pat4 = /\A(
|
116
|
-
@
|
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
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
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
|
-
|
235
|
-
|
236
|
-
|
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=
|
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,
|
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,
|
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
|
-
|
445
|
-
|
446
|
-
|
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
|
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 (
|
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
|
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
|
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.
|
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
|
-
|
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.
|
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:
|
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.
|
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.
|
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:
|
51
|
+
version: 2.4.0
|
64
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
53
|
requirements:
|
66
54
|
- - ">="
|