mymatrix 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+