mymatrix 0.0.1

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.
data/.gemtest ADDED
File without changes
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2012-03-24
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/mymatrix.rb
7
+ script/console
8
+ script/destroy
9
+ script/generate
10
+ test/test_helper.rb
11
+ test/test_mymatrix.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on mymatrix, see http://mymatrix.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,50 @@
1
+ = mymatrix
2
+
3
+
4
+
5
+ == DESCRIPTION:
6
+
7
+ mymatrix is a handling library for MS Excel and csv/tsv text.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Support filetypes: .xls, .tsv, .csv
12
+
13
+
14
+ == SYNOPSIS:
15
+
16
+
17
+
18
+ == REQUIREMENTS:
19
+
20
+ Support ruby versions are:
21
+ ruby 1.8.7
22
+ ruby 1.9.2 or higher
23
+
24
+ == INSTALL:
25
+ gem install mymatrix
26
+
27
+ == LICENSE:
28
+
29
+ (The MIT License)
30
+
31
+ Copyright (c) 2012 zucay
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining
34
+ a copy of this software and associated documentation files (the
35
+ 'Software'), to deal in the Software without restriction, including
36
+ without limitation the rights to use, copy, modify, merge, publish,
37
+ distribute, sublicense, and/or sell copies of the Software, and to
38
+ permit persons to whom the Software is furnished to do so, subject to
39
+ the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be
42
+ included in all copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
45
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
46
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
47
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
48
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
49
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
50
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/mymatrix'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'mymatrix' do
14
+ self.developer 'zucay', 'yukihico@gmail.com'
15
+
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ # self.extra_deps = [['activesupport','>= 2.0.2']]
18
+
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # remove_task :default
26
+ # task :default => [:spec, :features]
data/lib/mymatrix.rb ADDED
@@ -0,0 +1,1235 @@
1
+ #!/usr/bin/ruby -Ku
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ $:.unshift(File.dirname(__FILE__)) unless
5
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
6
+
7
+ require 'rubygems'
8
+ require 'spreadsheet'
9
+ require 'nkf'
10
+ require 'logger'
11
+ require 'pp'
12
+ require 'enumerable_ex' #verbose_each
13
+
14
+ if(RUBY_VERSION =~ /1\.[^9]/)
15
+ $KCODE='UTF8'
16
+ end
17
+
18
+ class MyMatrix
19
+ VERSION = '0.0.1'
20
+
21
+ attr_accessor :file, :internal_lf, :mx
22
+ include Enumerable
23
+ #to_t()の際のセパレータ。
24
+ SEPARATOR = "\t"
25
+
26
+ # コンストラクタ
27
+ # ====Args
28
+ # _file_ :: オープンするファイル。
29
+ # ====Return
30
+ # 生成されたMyMatrixオブジェクト
31
+ def initialize(file=nil, opts={})
32
+ #platform check
33
+ if(RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|cygwin|bccwin/)
34
+ $mymatrix_filesystem = 's'
35
+ elsif(RUBY_PLATFORM.downcase =~ /darwin/)
36
+ $mymatrix_filesystem = 'm'
37
+ elsif(RUBY_PLATFORM.downcase =~ /linux/)
38
+ $mymatrix_filesystem = 'u'
39
+ else
40
+ $mymatrix_filesystem = 'u'
41
+ end
42
+
43
+
44
+
45
+ #内部改行コード。
46
+ @internal_lf = '<br>'
47
+ rnd = rand(9999)
48
+ i = 0
49
+ begin
50
+ @log = Logger.new("MyMatrix_ERR_#{i}.log")
51
+ rescue
52
+ i += 1
53
+ retry
54
+ end
55
+ @log.level = Logger::DEBUG
56
+ @file = file
57
+
58
+ @mx = []
59
+ if(@file =~ /\.xls$/)
60
+ @mx = makeMatrixFromXLS(@file, opts)
61
+ elsif(@file =~ /(\.tsv|\.txt|\.TSV|\.TXT)/)
62
+ @mx = makeMatrixFromTSV(@file, opts)
63
+ elsif(@file =~ /(\.csv|\.CSV)/)
64
+ #opts[:sep] = ','
65
+ #@mx = makeMatrixFromTSV(@file, opts)
66
+ @mx = makeMatrixFromCSV(@file, opts)
67
+
68
+ # elsif(@file =~ /\.mmx$/)
69
+ # readmx = Marshal.load(open(@file).read)
70
+ # return readmx
71
+ elsif(@file == nil)
72
+ else
73
+ #デフォルトはTSVで読み込むようにする。
74
+ @mx = makeMatrixFromTSV(@file, opts)
75
+ end
76
+
77
+ #@mxの末尾に空レコードが入っていたら、その空白を削除
78
+ while(@mx[@mx.size-1] && @mx[@mx.size-1].join == '')
79
+ @mx.pop
80
+ end
81
+ if(@mx.size == 0)
82
+ @mx = []
83
+ end
84
+ @headers = @mx.shift
85
+ registerMatrix
86
+ return self
87
+ end
88
+
89
+ # CP932を正しく扱うための変換関数
90
+ # ====Args
91
+ # _str_ :: UTF8文字列
92
+ # ====Return
93
+ # CP932の文字列
94
+ def self.tosjis(str)
95
+ str = MyMatrix.cp932ize(str)
96
+ if(RUBY_VERSION =~ /1\.[^9]/)
97
+ #-xは半角カナを全角にするのを抑止するオプション。
98
+ out = NKF.nkf('-W -x -s --cp932', str)
99
+ else
100
+ out = str.encode("Windows-31J")
101
+ end
102
+
103
+
104
+ return out
105
+ end
106
+ # 外部ファイルエンコード(CP932)を内部エンコード(UTF8)に変換する
107
+ # ====Args
108
+ # _str_:: CP932 string
109
+ # ====Return
110
+ # UTF8 String
111
+ def self.toutf8(str)
112
+ #out = NKF.nkf('-x -w --cp932', str)
113
+ #入力がShift-jisであるとする。
114
+ out = NKF.nkf('-S -x -w --cp932', str)
115
+ return out
116
+ end
117
+
118
+ # MacOSXのファイルシステムで使われるUTF8-Mac(BOMつきUTF)に変換する
119
+ def self.toUtf8Mac(str)
120
+ out = str
121
+ return out
122
+ end
123
+ # ファイルオープン時、パス文字列のエンコードを変換してシステムに返却するためのメソッド
124
+ def encodePath(path)
125
+ case $mymatrix_filesystem
126
+ when 'u'
127
+ #utf8=>utf8なので何もしない
128
+ #path = MyMatrix.toutf8(path)
129
+ #path.encode('UTF-8')
130
+ path
131
+ when 's'
132
+ path = MyMatrix.tosjis(path)
133
+ #path.encode('Windows-31J')
134
+ when 'w'
135
+ path = MyMatrix.tosjis(path)
136
+ #path.encode('Windows-31J')
137
+ when 'm'
138
+ path = MyMatrix.toUtf8Mac(path)
139
+ end
140
+ end
141
+
142
+ #--
143
+ #protected methods
144
+ #++
145
+ protected
146
+ # 現在のヘッダ@headersに合わせてハッシュ@headerHを生成する
147
+ # @headersを書き換えた場合に必ず実行するメソッド
148
+ def registerMatrix
149
+ @headerH = Hash.new
150
+ if(!@headers)
151
+ @headers = []
152
+ end
153
+ @headers.each_with_index do |colName, i|
154
+ @headerH[colName] = i
155
+ end
156
+ fillEmptyCell
157
+ end
158
+
159
+ # ヘッダ@headers を書き換えた場合など、@headers.sizeとrow.sizeが異なる場合にサイズを揃え
160
+ # 空文字列を入れておく。getValue()した際に必ずStringを返却するための処理。
161
+ # ====※rowを伸ばす方向のみ。@headersを短くした場合の動作は保証できない!
162
+ #
163
+ def fillEmptyCell
164
+ headerSize = getHeaders.size
165
+ @mx.each_with_index do |row, i|
166
+ if(row.size < headerSize)
167
+ (headerSize - row.size).times do |i|
168
+ row << ''
169
+ end
170
+ elsif(row.size > headerSize)
171
+ warn("row is large:#{@file} line #{i+2} / rowSize #{row.size} / headersize #{headerSize}")
172
+ #raise "rowsize error"
173
+ end
174
+ end
175
+ end
176
+ # xls読み込みメソッド
177
+ def makeMatrixFromXLS(xlsFile, opts={})
178
+ if(opts)
179
+ offset = opts[:offset]
180
+ end
181
+ offset ||= 0
182
+
183
+ out = []
184
+ #todo xlsFileがなかったら作成
185
+ encodePath(xlsFile)
186
+ xl = Spreadsheet.open(encodePath(xlsFile), 'rb')
187
+ sheet = xl.worksheet(0)
188
+ rowsize = sheet.last_row_index
189
+ (rowsize+1-offset).times do |i|
190
+ row = sheet.row(i+offset)
191
+ orow = []
192
+ row.each do |ele|
193
+ #様々な型で値が入っている。改行も入っている
194
+ if(ele.class == Float)&&(ele.to_s =~ /(\d+)\.0/)
195
+ ele = $1
196
+ end
197
+ if(ele.class == Spreadsheet::Formula)
198
+ ele = ele.value
199
+ end
200
+ if(ele == nil)
201
+ ele = ''
202
+ end
203
+ ele = ele.to_s.gsub(/\n/, '<br>')
204
+ orow << ele
205
+ end
206
+ out << orow
207
+ end
208
+
209
+ return out
210
+ end
211
+ #TSV: tab separated value 読み込みメソッド
212
+ def makeMatrixFromTSV(file, opts={:sep=>SEPARATOR, :offset=>0})
213
+ out = []
214
+ epath = encodePath(file)
215
+ if(!File.exist?(epath))
216
+ open(epath, 'w') do |fo|
217
+ fo.print("\n\n")
218
+ end
219
+ end
220
+ #fi = open(file.encode('Windows-31J'), "r:Windows-31J")
221
+ fi = open(encodePath(file), "r:Windows-31J")
222
+ if(opts[:offset])
223
+ opts[:offset].times do |i|
224
+ fi.gets
225
+ end
226
+ end
227
+ opts[:sep]||=SEPARATOR
228
+ fi.each do |line|
229
+ row = MyMatrix.toutf8(line).chomp.split(/#{opts[:sep]}/)
230
+ #「1,300台」などカンマが使われている場合、「"1,300台"」となってしまうので、カンマを無視する
231
+ newRow = []
232
+ row.each do |cell|
233
+ stri = cell.dup
234
+ stri.gsub!(/^\"(.*)\"$/, '\1')
235
+ #"
236
+ stri.gsub!(/""/, '"')
237
+ newRow << stri
238
+ end
239
+ out << newRow
240
+ end
241
+ fi.close
242
+ return out
243
+ end
244
+
245
+ #CSV読み込みメソッド
246
+ def makeMatrixFromCSV(file, opts={:offset=>0})
247
+ #1.9系ではFasterCSVを使えない
248
+ if(RUBY_VERSION =~ /1\.[^9]/)
249
+ #1.8以下の場合
250
+ require 'fastercsv'
251
+ csv = FasterCSV
252
+ else
253
+ #1.9以上の場合
254
+ require 'csv'
255
+ Encoding.default_external = 'Windows-31J'
256
+ csv = CSV
257
+ end
258
+ out = []
259
+ i= 0
260
+ syspath = encodePath(file)
261
+ csv.foreach(syspath, {:row_sep => "\r\n", :encoding => 'Shift_JIS'}) do |row|
262
+ if(opts[:offset])
263
+ if(opts[:offset] < i)
264
+ next
265
+ end
266
+ end
267
+ #「1,300台」などカンマが使われている場合、「"1,300台"」となってしまうので、カンマを無視する
268
+ newRow = []
269
+ row.each do |cell|
270
+ cell = cell.to_s
271
+ cell ||= ''
272
+ cell = MyMatrix.toutf8(cell)
273
+ #cell = cell.gsub(/^\"/, "")
274
+ #cell = cell.gsub(/\"$/, "")
275
+ #"
276
+ newRow << cell
277
+ end
278
+ out << newRow
279
+ i += 1
280
+ end
281
+ return out
282
+ end
283
+
284
+ def isEnd(row)
285
+ out = true
286
+ row.each do |cell|
287
+ if(cell != "")
288
+ out = nil
289
+ break
290
+ end
291
+ end
292
+ return out
293
+ end
294
+
295
+ public
296
+ # カラムの値を配列で返却する
297
+ def getColumn(colName)
298
+ out = []
299
+ @mx.each do |row|
300
+ begin
301
+ out << getValue(row, colName)
302
+ rescue
303
+ raise "#{colName} notfound: #{row}"
304
+ end
305
+ end
306
+ return out
307
+ end
308
+
309
+ # getColumn のエイリアスメソッド
310
+ def getValues(colName)
311
+ return getColumn(colName)
312
+ end
313
+ #カラムの値を複数指定して、多次元配列を返却するメソッド。
314
+ def getColumns(colNames)
315
+ out = []
316
+ colNames.each do |colName|
317
+ out << getColumn(colName)
318
+ end
319
+ return out
320
+ end
321
+
322
+ #
323
+ def getColumnsByMatrix(colNames)
324
+ out = MyMatrix.new
325
+ colNames.each do |colName|
326
+ col = getColumn(colName)
327
+ out.addColumn(colName, col)
328
+ end
329
+ return out
330
+ end
331
+ def val(row, str)
332
+ getValue(row, str)
333
+ end
334
+
335
+ def getValue(row, str)
336
+ out = nil
337
+ index = @headerH[str]
338
+ if(index)
339
+ out = row[index]
340
+ #お尻のセルでNULLの場合などは、nilが返却されてしまう。なので、''となるようにする。
341
+ if(!out)
342
+ out = ''
343
+ end
344
+ #参照を渡さないためdupする
345
+ out = out.dup
346
+ else
347
+ raise "header not found:#{str} file:#{@file}"
348
+ end
349
+ return out
350
+ end
351
+ alias val getValue
352
+ =begin
353
+ def getValues(row, arr)
354
+ out = []
355
+ arr.each do |ele|
356
+ out << getValue(row, ele)
357
+ end
358
+ if(out.size == 0)
359
+ out = nil
360
+ end
361
+ return out
362
+ end
363
+ =end
364
+ def setValue(row, str, value)
365
+ if(!row)
366
+ raise 'row is nil'
367
+ end
368
+ index = @headerH[str]
369
+ if(!index)
370
+ addHeaders([str])
371
+ end
372
+ #参照先の値も変更できるように、破壊メソッドを使う。row[@headerH[str]] = valueでは、参照先が切り替わってしまうので、値の置き換えにならない。
373
+ #findなどで取得したrowに対して処理を行う際に必要な変更。
374
+ if(row[@headerH[str]].class == String)
375
+ row[@headerH[str]].sub!(/^.*$/, value)
376
+ else
377
+ #raise('not string error.')
378
+ #todo 強烈なバグな気もするが、例外を回避し値を代入2010年12月15日
379
+ begin
380
+ row[@headerH[str]] = value.to_s
381
+ rescue
382
+ row[@headerH[str]] = ''
383
+ end
384
+ end
385
+ end
386
+ def each
387
+ @mx.each do |row|
388
+ yield(row)
389
+ end
390
+ end
391
+ #未検証
392
+ def reverse
393
+ out = empty
394
+
395
+ @mx.reverse.each do |row|
396
+ out << row
397
+ end
398
+ return out
399
+ end
400
+
401
+
402
+ def size
403
+ return @mx.size
404
+ end
405
+ def [](i,j)
406
+ return @mx[i][j]
407
+ end
408
+
409
+ def [](i)
410
+ return @mx[i]
411
+ end
412
+
413
+ #未検証
414
+ def +(other)
415
+ out = MyipcMatrix.new
416
+
417
+ othHeaders = other.getHeaders
418
+ selHeaders = getHeaders
419
+
420
+ selHeaders.each do |header|
421
+ out.addColumn(header, getColumn(header))
422
+ end
423
+
424
+ othHeaders.each do |header|
425
+ out.addColumn(header, other.getColumn(header))
426
+ end
427
+
428
+ return out
429
+ end
430
+
431
+ def addColumn(header, column)
432
+ pushColumn(header, column)
433
+ end
434
+ def <<(row)
435
+ addRow(row)
436
+ end
437
+ def addRow(row)
438
+ if(row.class != Array)
439
+ row = [row]
440
+ end
441
+ row.size.times do |i|
442
+ if(row[i] == nil)
443
+ row[i] = ''
444
+ end
445
+ end
446
+
447
+ headerSize = getHeaders.size
448
+ rowSize = row.size
449
+ if(headerSize > rowSize)
450
+ (headerSize - rowSize).times do |i|
451
+ row << ''
452
+ end
453
+ elsif(rowSize > headerSize)
454
+ raise("row size error. headerSize:#{headerSize} rowSize:#{rowSize}")
455
+ end
456
+ @mx << row.dup
457
+ end
458
+ def [](i)
459
+ return @mx[i]
460
+ end
461
+ def []=(key, value)
462
+ @mx[key] = value
463
+ end
464
+
465
+ def pushColumn(header, column)
466
+ colPos = @headers.length
467
+ @headers << header
468
+ registerMatrix
469
+ column.each_with_index do |cell, i|
470
+ if(@mx[i] == nil)
471
+ @mx[i] = []
472
+ end
473
+ @mx[i][colPos] = cell
474
+ end
475
+ end
476
+ #使い勝手が良くないので気をつけて使う(todo参照)
477
+ def unShiftColumn(header, column)
478
+ @headers.unshift(header)
479
+ registerMatrix
480
+ column.each_with_index do |cell, i|
481
+ if(@mx[i] == nil)
482
+ @mx[i] = []
483
+ end
484
+ #todo:ヘッダよりでかいrowがある場合バグる。期待していない一番右の値が取れてしまう。
485
+ @mx[i].unshift(cell)
486
+ end
487
+ end
488
+
489
+ def shiftColumn()
490
+ header = @headers.shift
491
+ column = []
492
+ registerMatrix
493
+ @mx.each do |row|
494
+ column << row.shift
495
+ end
496
+ return header, column
497
+ end
498
+
499
+ #複数に分割されたテキストファイルを出力する
500
+ def divide(splitNum)
501
+ lineNum = (@mx.size / splitNum) + 1
502
+ mymxs = []
503
+ tmp = MyMatrix.new
504
+ tmp.file = @file
505
+ tmp.addHeaders(getHeaders)
506
+ @mx.each_with_index do |row, i|
507
+ tmp << row.dup
508
+ if((i+1) % lineNum == 0)
509
+ mymxs << tmp
510
+ tmp = MyMatrix.new
511
+ tmp.addHeaders(getHeaders)
512
+ tmp.file = @file
513
+ end
514
+ end
515
+ mymxs << tmp
516
+ mymxs.each_with_index do |mymx, i|
517
+ p i
518
+ mymx.to_t_with("#{i}")
519
+ end
520
+ end
521
+ #ファイルの中身をSJISにするために使ってる
522
+ def localEncode(v, enc = 's')
523
+ case enc
524
+ when 'u'
525
+ str = MyMatrix.toutf8(v)
526
+ when 's'
527
+ str = MyMatrix.tosjis(v)
528
+ else
529
+ str = MyMatrix.tosjis(v)
530
+ end
531
+ end
532
+ #使い方はto_t()を参照。yield。
533
+ def to_text(outFile)
534
+ outFile = encodePath(outFile)
535
+ out = []
536
+ out << @headers
537
+ @mx.each do |row|
538
+ out << row
539
+ end
540
+ begin
541
+ fo = open(outFile, 'wb')
542
+ rescue => e
543
+ p "cannot write file...#{outFile}"
544
+ p e
545
+ sleep(5)
546
+ retry
547
+ end
548
+ out.each_with_index do |row, i|
549
+ if(row == nil)
550
+ warn("line #{i} is nil")
551
+ fo.print("")
552
+ else
553
+ str = yield(row)
554
+ fo.print(str)
555
+ end
556
+ fo.print("\r\n")
557
+ end
558
+ fo.close
559
+ end
560
+ # テキスト出力する
561
+ def to_t(outFile=nil, opts={})
562
+ if(!outFile)
563
+ outFile = @file
564
+ end
565
+
566
+ #拡張子の判別
567
+ ext = File.extname(outFile).downcase
568
+ case ext
569
+ when '.csv'
570
+ opts[:separator] ||= ','
571
+ opts[:escape] ||= true
572
+ when '.xls'
573
+ p 'use Tab-Separated-Value text format.'
574
+ outFile = outFile + '.txt'
575
+ when '.xlsx'
576
+ p 'use Tab-Separated-Value text format.'
577
+ outFile = outFile + '.txt'
578
+ else
579
+ #do nothing
580
+ end
581
+ #デフォルトオプションの設定
582
+ opts[:enc] ||= 's'
583
+ opts[:escape] ||= false
584
+ opts[:separator] ||= SEPARATOR
585
+
586
+ to_text(outFile) do |row|
587
+ orow = []
588
+ if(opts[:escape])
589
+ row.each do |cell|
590
+ orow << myescape(cell)
591
+ end
592
+ else
593
+ row.each do |cell|
594
+ orow << cell.to_s.gsub(/[#{opts[:separator]}\r\n]/, '')
595
+ end
596
+ end
597
+
598
+ begin
599
+ str = localEncode(orow.join(opts[:separator]), opts[:enc])
600
+ rescue Encoding::UndefinedConversionError
601
+ orow.each do |ele|
602
+ begin
603
+ localEncode(ele, opts[:enc])
604
+ rescue
605
+ raise "encode error.#{ele}\n(#{orow})"
606
+ end
607
+ end
608
+
609
+ @log.debug(row.join(opts[:separator]))
610
+ end
611
+ str
612
+ end
613
+ end
614
+ def myescape(cell)
615
+ o = cell.to_s.dup
616
+ o.gsub!(/"/, '""')
617
+ if o =~ /[",']/
618
+ #'
619
+ o = "\"#{o}\""
620
+ end
621
+ return o
622
+ end
623
+ # 読み込んだファイル名にpostfixを付与してテキストファイル出力する
624
+ def to_t_with(postfix="out", opts={})
625
+ opath = MyMatrix.make_path(@file, {:ext=>nil, :postfix=>postfix})
626
+ to_t(opath, opts)
627
+ return opath
628
+ end
629
+
630
+ #CSV出力する。ダブルクオーテーションやカンマをエスケープする。
631
+ def to_csv(outFile)
632
+ to_t(outFile, {:separator=>',', :escape=>true})
633
+ end
634
+
635
+ #ヘッダのコピーを返却する
636
+ def getHeaders
637
+ out = @headers.dup
638
+ return out
639
+ end
640
+ #ヘッダを置き換える
641
+ def replaceHeader(before, after)
642
+ @headers[@headerH[before]] = after
643
+ registerMatrix
644
+ end
645
+
646
+ # colNameの値がvalueの行のうち先頭のものを返却する。todo:名称がよくない。
647
+ def index(colName, value)
648
+ out = nil
649
+ col = getColumn(colName)
650
+ col.each_with_index do |cell, i|
651
+ if(value == cell)
652
+ out = i
653
+ break
654
+ end
655
+ end
656
+ return out
657
+ end
658
+
659
+ # colnameの値がvalueの行のもののインデックス値を配列で返却する。todo:名称がよくない。
660
+ def searchIndexes(colName, value)
661
+ out = []
662
+ col = getColumn(colName)
663
+ col.each_with_index do |cell, i|
664
+ if(value == cell)
665
+ out << i
666
+ end
667
+ end
668
+ return out
669
+ end
670
+
671
+ # colnameの値がvalueの行のrowを配列で返却する。todo:名称がよくない。
672
+ def search(colName, value)
673
+ indexes = []
674
+ col = getColumn(colName)
675
+ col.each_with_index do |cell, i|
676
+ if(value == cell)
677
+ indexes << i
678
+ end
679
+ end
680
+ out = self.empty
681
+ indexes.each do |index|
682
+ out << @mx[index]
683
+ end
684
+ return out
685
+ end
686
+ # ヘッダを追加する(配列)
687
+ def addHeaders(aheaders)
688
+ @headers.concat(aheaders).uniq!
689
+
690
+ registerMatrix
691
+ end
692
+
693
+ # ヘッダを追加する
694
+ def addHeader(key)
695
+ addHeaders([key])
696
+ end
697
+
698
+ #行数を返却する
699
+ def size
700
+ return @mx.size
701
+ end
702
+
703
+ #未検証。「要素の重複判定は、Object#eql? により行われます。」http://www.ruby-lang.org/ja/old-man/html/Array.html#uniq
704
+ def uniq!
705
+ @mx.uniq!
706
+ end
707
+
708
+ def shift
709
+ return @mx.shift
710
+ end
711
+ def unshift(var)
712
+ return @mx.unshift(var)
713
+ end
714
+ def pop
715
+ return @mx.pop
716
+ end
717
+ def push(var)
718
+ return @mx.push(var)
719
+ end
720
+ def delete_at(pos)
721
+ @mx.delete_at(pos)
722
+ end
723
+
724
+ #未検証。「要素を順番にブロックに渡して評価し、その結果が真になった要素を すべて削除します。」
725
+ def delete_if
726
+ out = @mx.delete_if do |row|
727
+ yield(row)
728
+ end
729
+ @mx = out
730
+ end
731
+ def delete(v)
732
+ @mx.delete(v)
733
+ end
734
+ #ブロックがTrueになる、配列(参照)を返却するメソッド
735
+ def find
736
+ #todo rowsを返却するのと、Mymatrxixを返却するのとどっちがイイのか。。
737
+ rows = []
738
+ @mx.each do |row|
739
+ if(yield(row))
740
+ rows << row.dup
741
+ end
742
+ end
743
+ return rows
744
+ end
745
+
746
+ #headersに記載の
747
+ def select(headers)
748
+ out = self.class.new
749
+ headers.each do |header|
750
+ out.addColumn(header, getColumn(header))
751
+ end
752
+ out.file = @file
753
+ return out
754
+ end
755
+
756
+ #fromColName => toColNameのハッシュを作成する。
757
+ #fromColNameの値が同じだとtoColNameが上書きされるので使いにくいと思われる。
758
+ def makeHash(fromColName, toColName)
759
+ out = Hash.new
760
+ @mx.each do |row|
761
+ from = getValue(row, fromColName)
762
+ to = getValue(row, toColName)
763
+ out[from] = to
764
+ end
765
+ return out
766
+ end
767
+
768
+ #colnameがキーのハッシュを作る。valueはrowの配列。
769
+ def makeKey(colname)
770
+ out = {}
771
+ self.each do |row|
772
+ key = self.val(row, colname)
773
+ if(out[key] == nil)
774
+ out[key] = []
775
+ end
776
+ out[key] << row
777
+ end
778
+ return out
779
+ end
780
+
781
+ #MyipcMatrixとの互換性のため。getValueのエイリアス
782
+ def getCrrValue(row, str)
783
+ p 'this class is not MyipcMatrix.'
784
+ getValue(row, str)
785
+ end
786
+
787
+ def concatCells(headers, colname)
788
+ addHeaders([colname])
789
+ @mx.each do |row|
790
+ val = []
791
+ headers.each do |header|
792
+ val << getValue(row, header)
793
+ end
794
+ setValue(row, colname, val.join('_').gsub(/_+/, '_'))
795
+ end
796
+ end
797
+
798
+ #読み込んだファイルのパスを返却する
799
+ def getPath
800
+ return @file
801
+ end
802
+ #ファイルパスを設定する。 to_tを引数なしで使うと、設定したパスにファイルが生成される。
803
+ def setPath(path)
804
+ @file = path
805
+ end
806
+
807
+ # strを含む(/#{str}/)ヘッダ名を配列で返却する。
808
+ def searchHeader(str)
809
+ out = []
810
+ getHeaders.each do |header|
811
+ if(header =~ /#{str}/)
812
+ out << header
813
+ end
814
+ end
815
+ end
816
+
817
+ #n分割した配列を返却する
818
+ def devide(n)
819
+ out = []
820
+ mx = @mx.dup
821
+ eleSize = mx.size/n
822
+ n.times do |i|
823
+ o = self.empty
824
+ eleSize.times do |j|
825
+ o << mx.shift
826
+ end
827
+ out << o
828
+ end
829
+ #@mx.size%n分余ってるので、追加
830
+ mx.each do |ele|
831
+ out[n-1] << ele
832
+ end
833
+ return out
834
+ end
835
+ #compareHeaderの値の中に、valuesに書かれた値があったら、targetHeaderにフラグを立てる
836
+ def addFlg(targetHeader, compareHeader, values, flgValue='1')
837
+ compares = getColumn(compareHeader)
838
+ values.each do|value|
839
+ i = compares.index(value)
840
+ if(i)
841
+ setValue(@mx[i], targetHeader, flgValue)
842
+ else
843
+ #raise "VALUE NOT FOUND:#{value}"
844
+ end
845
+ end
846
+ end
847
+
848
+ def without(regexp_or_string)
849
+ if(regexp_or_string.class == String)
850
+ regexp = /#{regexp_or_string}/
851
+ else
852
+ regexp = regexp_or_string
853
+ end
854
+ newHeaders = []
855
+ @headers.each do |header|
856
+ if(header =~ regexp)
857
+ else
858
+ newHeaders << header
859
+ end
860
+ end
861
+ out = select(newHeaders)
862
+ return out
863
+ end
864
+
865
+ #行が空(ヘッダはそのまま)のコピーを返却する
866
+ def empty
867
+ out = self.dup
868
+ out.empty!
869
+ return out
870
+ end
871
+
872
+ #selfの行を空にする
873
+ def empty!
874
+ @mx = []
875
+ return self
876
+ end
877
+ def fill(rows)
878
+ rows.each do |row|
879
+ self << row
880
+ end
881
+ return self
882
+ end
883
+
884
+ #行番号を付与する
885
+ def with_serial(headerName = 'No.')
886
+ out = self.empty
887
+ out.addHeaders([headerName], 1)
888
+ self.each_with_index do |row, i|
889
+ no = i + 1
890
+ newRow = [no].concat(row)
891
+ out << newRow
892
+ end
893
+ return out
894
+ end
895
+
896
+
897
+ def count(header, value)
898
+ out = 0
899
+ arr = getColumn(header)
900
+ arr.each do |ele|
901
+ if(ele =~ /#{value}/)
902
+ out += 1
903
+ end
904
+ end
905
+ return out
906
+ end
907
+ #全件カウントして、[value, count] という配列に格納する
908
+ def countup(header)
909
+ out = []
910
+ values = getColumn(header).uniq
911
+ values.each do |value|
912
+ out << [value, self.count(header, value)]
913
+ end
914
+ return out
915
+ end
916
+ def getDoubles(arr)
917
+ doubles = arr.select do |e|
918
+ arr.index(e) != arr.rindex(e)
919
+ end
920
+ doubles.uniq!
921
+ return doubles
922
+ end
923
+
924
+ def filter(header, value)
925
+ out = empty
926
+ @mx.each do|row|
927
+ v = getValue(row, header)
928
+ if(v == value)
929
+ out << row
930
+ end
931
+ end
932
+ return out
933
+ end
934
+ #配列と引き算とかする際に使われる。
935
+ def to_ary
936
+ arr = []
937
+ @mx.each do |row|
938
+ #arr << row.dup
939
+ arr << row
940
+ end
941
+ return arr
942
+ end
943
+ def to_s
944
+ out = ''
945
+ @mx.each do |row|
946
+ out = out + row.to_s + "\n"
947
+ end
948
+ return out
949
+ end
950
+ def to_s_with_header
951
+ out = self.getHeaders.to_s + "\n"
952
+ out = out + self.to_s
953
+ end
954
+ def concat(mx, opt={})
955
+ notfoundHeaders = []
956
+ mx.each do |row|
957
+ o = []
958
+ mx.getHeaders.each do |head|
959
+ begin
960
+ self.setValue(o, head, mx.getValue(row, head))
961
+ rescue => e
962
+ #p e
963
+ if(opt[:loose]==true)
964
+ self.addHeaders([head])
965
+ #p "#{head} added"
966
+ retry
967
+ else
968
+ notfoundHeaders << head
969
+ end
970
+ end
971
+ end
972
+ self << o
973
+ end
974
+ if(notfoundHeaders.size > 0)
975
+ raise "notfoundHeader : #{notfoundHeaders.uniq.join(',')}"
976
+ end
977
+ return self
978
+ end
979
+ def concatFile(file, opt={})
980
+ #p file
981
+ mx = MyMatrix.new(file)
982
+ self.concat(mx, opt)
983
+ return self
984
+ end
985
+ #フォルダ内ファイルの結合。絶対パスを指定する
986
+ def concatDir(dir)
987
+ dir = File.expand_path(dir)
988
+ Dir.entries(dir).each do |ent|
989
+ if(ent =~ /^\./)
990
+ else
991
+ #p ent
992
+
993
+ file = dir + '/' + ent
994
+ #p "concat:#{file}"
995
+ nmx = MyMatrix.new(file)
996
+ self.concat(nmx)
997
+ end
998
+ end
999
+
1000
+ end
1001
+ =begin
1002
+ def concat!(mx)
1003
+ o = self.concat(mx)
1004
+ self = o
1005
+ return self
1006
+ end
1007
+ def concatFile!(file)
1008
+ o = self.concatFile(file)
1009
+ self = o
1010
+ return self
1011
+ end
1012
+ =end
1013
+ def flushCol(colname)
1014
+ @mx.each do |row|
1015
+ self.setValue(row, colname, '')
1016
+ end
1017
+ return self
1018
+ end
1019
+ def sortBy(colname, reverse=false)
1020
+ sortmx = []
1021
+ self.each do |row|
1022
+ key = self.getValue(row, colname)
1023
+ sortmx << [key, row]
1024
+ end
1025
+ sortmx.sort!
1026
+ self.empty!
1027
+ sortmx.each do |keyrow|
1028
+ self << keyrow[1]
1029
+ end
1030
+ return self
1031
+ end
1032
+ def dupRow(row, destmx, destrow, headers)
1033
+ headers.each do |head|
1034
+ val = self.getValue(row, head)
1035
+ destmx.setValue(destrow, head, val)
1036
+ end
1037
+ return destrow
1038
+ end
1039
+ def setSame(head, headValue, hash)
1040
+ idxs = self.searchIndexes(head, headValue)
1041
+ idxs.each do |idx|
1042
+ hash.each_pair do |key, value|
1043
+ self.setValue(@mx[idx], key, value)
1044
+ end
1045
+ end
1046
+ end
1047
+
1048
+ #都道府県市区町村コードの桁そろえ
1049
+ #Excelで先頭の0が落ちて桁が変わるため
1050
+ def correctCityCodes!(colname = '都道府県市区町村コード')
1051
+ self.each do |row|
1052
+ code = self.val(row, colname)
1053
+ if(code.length == 4)
1054
+ self.setValue(row, colname, sprintf("%05d", code))
1055
+ elsif(code.length == 5)
1056
+ #correct
1057
+ else
1058
+ raise "Citycode length error. '#{code}'"
1059
+ end
1060
+ end
1061
+ return self
1062
+ end
1063
+
1064
+ #末尾空白の削除
1065
+ def delEndSp!
1066
+ self.each do |row|
1067
+ self.getHeaders.each do |head|
1068
+ val = self.val(row, head)
1069
+ if(val =~ /(.*)[  ]$/)
1070
+ self.setValue(row, head, $1)
1071
+ end
1072
+ end
1073
+ end
1074
+ return self
1075
+ end
1076
+ #半角カタカナの全角化
1077
+ def twoByteKana!
1078
+ self.each do |row|
1079
+ self.getHeaders.each do |head|
1080
+ val = self.val(row, head)
1081
+ #if(val =~ /[-~]/)
1082
+ if(val =~ /[-]/)
1083
+ #p "#{self.file} #{val}"
1084
+ next
1085
+ end
1086
+ #nval = Kconv.kconv(val, Kconv::UTF8, Kconv::UTF8)
1087
+ #nval = NKF.nkf('-W -w -x --cp932', val)
1088
+
1089
+ nval = NKF.nkf('-W -w', val)
1090
+
1091
+ if(val!=nval)
1092
+ #p "#{val}=>#{nval}"
1093
+ end
1094
+ self.setValue(row, head, nval)
1095
+ end
1096
+ end
1097
+ return self
1098
+ end
1099
+ def self.cp932ize(str)
1100
+ out = str.dup
1101
+ cases = [
1102
+ #['−', '―'], #MINUS SIGN(U+2212) to FULLWIDTH HYPHEN-MINUS(U+2015)(windows)
1103
+ #↑仕様としては上記が正しいが、運用上MINUS SIGN(U+2212) は FULLWIDTH HYPHEN-MINUS(U+FF0D)に変換する
1104
+ #キー入力時にMacとWindowsで同じ文字コードとなることが望ましいため。
1105
+
1106
+ ['〜','~'], #WAVE DASH (U+301C) to FULLWIDTH TILDE(U+FF5E)(windows)
1107
+ ['‖','∥'], #DOUBLE VERTICAL LINE (U+2016, "‖") を PARALLEL TO (U+2225, "∥") に
1108
+ ['—', '―'], #EM DASH (U+2014, "—") を HORIZONTAL BAR (U+2015, "―") に
1109
+ #以下、キー入力を想定した変換。
1110
+ ['ー', 'ー'], #MacのハイフンF7(google ime)→Windows(googleime):同じ
1111
+ ['ー', 'ー'], #MacのハイフンF8(google ime)→Windows(googleime):同じ
1112
+ ['−', '-'], #MacのハイフンF9[−](google ime)→Windows[-](googleime):違う。MINUS SIGN(U+2212) to FULLWIDTH HYPHEN-MINUS(U+FF0D)
1113
+ ['-', '-'], #MacのハイフンF10(google ime)→Windows(googleime):同じ
1114
+ #ユニコード固有文字:ノーブレークスペース
1115
+ ['[\u00A0]', ' '],
1116
+ #yen
1117
+ ['[\u00A5]', '¥'],
1118
+ # éとè:eの上に´と`
1119
+ ['[\u00E9]', 'e'],['[\u00E8]', 'e'],
1120
+ #spaces
1121
+ ['[\u2000]', ' '],['[\u2001]', ' '],['[\u2002]', ' '],['[\u2003]', ' '],['[\u2004]', ' '],['[\u2005]', ' '],['[\u2006]', ' '],['[\u2007]', ' '],['[\u2008]', ' '],['[\u2009]', ' '],['[\u200A]', ' '],['[\u205F]', ' ']
1122
+ ]
1123
+ cases.each do |c|
1124
+ out.gsub!(/#{c[0]}/, c[1])
1125
+ end
1126
+ return out
1127
+ end
1128
+ # def save(file)
1129
+ # p Marshal.dump(self)
1130
+ # open(file, 'w') do |fo|
1131
+ # fo.write(Marshal.dump(self))
1132
+ # end
1133
+ # end
1134
+
1135
+ =begin
1136
+ def to_xls(opts)
1137
+ if(opts[:out] =~ /.xls$/)
1138
+ else
1139
+ raise "not outfile"
1140
+ end
1141
+ opts[:template] ||= opts[:out]
1142
+
1143
+ opts[:offset_r] ||= 0
1144
+ opts[:offset_c] ||= 0
1145
+
1146
+ xl = Spreadsheet.open(encodePath(opts[:template]), 'r')
1147
+ sheet = xl.worksheet(0)
1148
+ @headers.each_with_index do |head, i|
1149
+ ab_row = opts[:offset_r]
1150
+ ab_col = opts[:offset_c] + i
1151
+ sheet[ab_row, ab_col] = head
1152
+ end
1153
+ self.each_with_index do |row, i|
1154
+ row.each_with_index do |cell, j|
1155
+ ab_row = opts[:offset_r] + i + 1
1156
+ #↑ヘッダ分1オフセット
1157
+ ab_col = opts[:offset_c] + j
1158
+ sheet[ab_row, ab_col] = cell
1159
+ end
1160
+ end
1161
+
1162
+ xl.write(opts[:out])
1163
+
1164
+ end
1165
+ =end
1166
+ #Arrayっぽく使うためのメソッド。内部の@mx.first
1167
+ def first
1168
+ @mx[0]
1169
+ end
1170
+ #Arrayっぽく使うためのメソッド。内部の@mx.last
1171
+ def last
1172
+ out = @mx[self.size-1]
1173
+ end
1174
+ def empty?
1175
+ @mx.empty?
1176
+ end
1177
+ #num文字以上の項目をnum文字に丸める
1178
+ def cutOff(head, num)
1179
+ self.each do |row|
1180
+ v = self.val(row, head)
1181
+ if(v =~ /(.{#{num}})/)
1182
+ self.setValue(row, head, $1)
1183
+ end
1184
+ end
1185
+ end
1186
+ def self.make_path(path, opts={ })
1187
+ # opath = makepath(@file, {:ext=>nil, :postfix=>postfix})
1188
+ dir = File.dirname(path)
1189
+ ext = opts[:ext]
1190
+ ext ||= File.extname(path)
1191
+ postfix = opts[:postfix].to_s
1192
+
1193
+ basename = File.basename(path, ".*")
1194
+ opath = (MyMatrix.new.encodePath("#{dir}/#{basename}_#{postfix}#{ext}"))
1195
+ end
1196
+ end
1197
+
1198
+ #ruby -Ks で利用する場合。ruby1.9では使えないはず。obsolete
1199
+ class SjisMyMatrix < MyMatrix
1200
+ def getValue(row, col)
1201
+ col = MyMatrix.toutf8(col)
1202
+ MyMatrix.tosjis(super(row, col))
1203
+ end
1204
+ def setValue(row, col, value)
1205
+ col = MyMatrix.toutf8(col)
1206
+ value = MyMatrix.toutf8(value)
1207
+ super(row, col, value)
1208
+ end
1209
+ def addHeaders(hs)
1210
+ arr =[]
1211
+ hs.each do |ele|
1212
+ arr << MyMatrix.toutf8(ele)
1213
+ end
1214
+ super(arr)
1215
+ end
1216
+ def getHeaders
1217
+ out = []
1218
+ arr = super()
1219
+ arr.each do |ele|
1220
+ out << MyMatrix.tosjis(ele)
1221
+ end
1222
+ return out
1223
+ end
1224
+ end
1225
+
1226
+
1227
+ #rails で使う場合。obsolete
1228
+ class MyRailsMatrix < MyMatrix
1229
+ def headers2db(t)
1230
+ getHeaders.each do |header|
1231
+ t.column header, :string
1232
+ end
1233
+ end
1234
+ end
1235
+