excelmap.rb
Excel ファイルをテキストファイルの内容で上書きするスクリプトを作った。関係ないセルはもちろんクリアしない。きっとどこかに存在して被ってるシリーズ(きっとというか被ってる人を知ってる)。
ソースはこの記事の末尾にあります。TODO もあるんで、知ってれば教えてくださると助かります。るびまで添削してもらおうかなぁ。
optparse って便利なんだけどしっくりこないなぁ……。
オプションはこんな感じ:
$ ruby excelmap.rb --help Usage: excelmap [options] -h, --help print this message -s SHEET_NO Sheet number -p Y,X starting Point -r ROW_SKIP_SIZE Row skip size -c COL_SKIP_SIZE Column skip size -t TERMINATOR split by Terminator -i, --ignore-empty-data Ignore empty data
ターミネイタじゃなくてセパレータの方がいいな……でも s が被りまくりだからいっか。
使い方:
タブ区切りとかカンマ区切りとかのテキストファイルを用意する。
0_0 0_1 0_2 0_3 0_4 1_0 1_2 1_4 2_0 2_1 2_2 2_3 2_4 3_0 3_2 3_4 4_0 4_1 4_2 4_3 4_4
実行。
$ ruby excelmap.rb -p3,2 -s2 -r3 -c1 test.txt test.xls
意味は、開始セルは3行・2列目、シートは2番目、3行飛び、1列飛びで test.txt の内容を test.xls にマップする。だ。
先ほどと同じオプションで実行。
$ ruby excelmap.rb -p3,2 -s2 -r3 -c1 test.txt test.xls
結果:
空白文字で上書きされちゃってる。通常動作はただのマッピング。有無を言わさずマッピング。
空白文字で上書きしたくない時は -i, --ignore オプションを用いる。またファイルを前の状態にもどして、
$ ruby excelmap.rb -p3,2 -s2 -r3 -c1 -i test.txt test.xls
これで年5回の単調で間違いやすい from テキスト to Excel 転記作業から開放される。今回は汎用的なので他の作業にも使えるな。何で今まで作らなかったか過去の自分を小一時間問い詰めたい。
TODO:
- ヘルプ表示の Usage 行に必須引数 "textfile excelfile" 表示を付け足す方法を見つける。
- Excel ファイルが前もって作成されてないと動かない。新規作成できるようにする。
- Application にならうなら Book クラスも作らないといけないんだけど、まぁいいや。
- 座標計算がバカですねぇ
- Delegate も豪快ですねぇ
- オプション処理なげぇ
一応ライセンス付けといた。Ruby's なので煮るなり焼くなり撒くなり。
ソース:
#!/usr/bin/env ruby require 'win32ole' require 'optparse' # License: Ruby's class WIN32OLE_EXT def WIN32OLE_EXT.get_absolute_path filename fso = WIN32OLE.new('Scripting.FileSystemObject') return fso.GetAbsolutePathName(filename) end end module Excel require 'delegate' class Application < SimpleDelegator def initialize visible @excel = (WIN32OLE.new('Excel.Application')) @excel.visible = visible super(@excel) begin yield self ensure @excel.Workbooks.Close @excel.Quit end end #module CONST; end #WIN32OLE.new.const_load(@excel, CONST) ##puts CONST.constants.each{|e| puts e + " = " + eval("CONST::#{e}").to_s} def open file path = WIN32OLE_EXT.get_absolute_path file @book = @excel.Workbooks.Open(path) begin yield @book ensure @book.Close(false) end end end end def parse_option argv options = { :col_skip_size => 0, :row_skip_size => 0, :sheet_no => 1, :py => 0, :px => 0, :term => "\t", :ignore => false, } opt = OptionParser.new opt.on('-h', '--help', 'print this message') { puts opt exit 0 } opt.on('-s SHEET_NO', 'Sheet number') { |v| options[:sheet_no] = v.to_i raise "sheet number should be over 1." unless v.to_i >= 1 } opt.on('-p Y,X', 'starting Point') { |v| if v =~ /(\d+),(\d+)/ options[:py] = $1.to_i options[:px] = $2.to_i else raise "#{v} can't point excel's cell." end } opt.on('-r ROW_SKIP_SIZE', 'Row skip size') { |v| options[:row_skip_size] = v.to_i } opt.on('-c COL_SKIP_SIZE', 'Column skip size') { |v| options[:col_skip_size] = v.to_i } opt.on('-t TERMINATOR', 'split by Terminator') { |v| options[:term] = v } opt.on('-i', '--ignore-empty-data', 'Ignore empty data') { options[:ignore] = true } opt.parse!(argv) unless argv.size == 2 puts opt exit 1 end options[:textfile] = argv.shift options[:excelfile] = argv.shift return options end if __FILE__ == $0 # option OPT = parse_option(ARGV) # read table = [] File.foreach(OPT[:textfile]) do |l| row = l.chomp.split(OPT[:term], -1) table << row end # write Excel::Application.new(false) { |xl| excelpath = WIN32OLE_EXT.get_absolute_path(OPT[:excelfile]) xl.open(excelpath) { |book| sheet = book.worksheets.item(OPT[:sheet_no]) table.each_with_index do |row, ri| y = OPT[:py] + (ri * (OPT[:row_skip_size] + 1)) row.each_with_index do |field, ci| x = OPT[:px] + (ci * (OPT[:col_skip_size] + 1)) next if field.nil? next if (field.empty? && OPT[:ignore]) sheet.Cells.Item(y, x).Value = field end end book.Save } } end