仕事でEXCELを弄るのでどの言語が楽ちんか比較してみた。
試したもの
言語 | ライブラリ |
---|---|
JScript(WSH) | activeX*1 |
ruby | win32ole |
python | win32com |
vbscript | createobject*2 |
php(PECL) | COM |
JavaScript(WSH)とRubyのソースファイルを発掘した。Javaも何処かにあったと思うんだけれど。見つからない。
次のようなシートをCSVにする。
作成日 | 更新日 | ファイル名 | 説明 | サイズ |
---|---|---|---|---|
2008-12-31 | 2008-12-31 | Sample.jpg | 兼六園にいったときの写真 | 122222 |
2008-12-31 | 2008-12-31 | 0801222.jpg | 兼六園にいったときの写真 家族全員で。 |
104532 |
- セル内部改行は<br>に置換。
- 空白はTrim。
- タブはスペースに置換。
EXCEL処理やバッチ処理に向いているスクリプト言語を考えてみました。
RubyでEXCEL処理サンプル
「るびま」とRubyOnWindows(Cuzic)を参考に。というか写経。
require 'win32ole' def getAbsPath filename fso = WIN32OLE.new('Scripting.FileSystemObject') return fso.GetAbsolutePathName(filename) end name = getAbsPath("Book1.xls") x1 = WIN32OLE.new('Excel.Application') book = x1.Workbooks.Open( name ) begin book.Worksheets.each do |sheet| sheet.UsedRange.Rows.each do | row | record = [] row.Columns.each do |cell| val = cell.Value val.gsub!(/\r\n|\r|\n/, '<br/>' ) if val != nil and val.class == String val.gsub!(/\t/, ' ' ) if val != nil and val.class == String val.strip! if val != nil and val.class == String if val then record << "'#{val}'" else record << "''" end end puts record.join(',') end end ensure book.Close x1.Quit end
JavaScript(WSH)でEXCEL
JScriptはWindowsでバッチに使える。COMも使える。当然EXCEL処理ができる。
Rubyサンプルと同じルーチンにした。WSHのCOMでは(for i in obj)でループを書けない。Enumerableを使うのがかなり面倒。
Enumerableがもっとシンプルになれば、JSでやる気がでる。JavaScriptなのでid:amachangあたりが何とかしてくれると期待。
//EXCEL処理サンプル function getAbsPath ( filename ){ fso = new ActiveXObject('Scripting.FileSystemObject') return fso.GetAbsolutePathName(filename) } var name = getAbsPath("Book1.xls") var x1 = new ActiveXObject("EXCEL.Application"); var book = x1.Workbooks.Open( name ); try { for( var p_sheet = new Enumerator( book.Worksheets );!p_sheet.atEnd();p_sheet.moveNext() ){ var sheet = p_sheet.item(); for(var p_row = new Enumerator(sheet.UsedRange.Rows);!p_row.atEnd();p_row.moveNext() ){ var row = p_row.item(); var record = []; for( var p_cell = new Enumerator(row.Columns);!p_cell.atEnd();p_cell.moveNext()){ var cell = p_cell.item(); val = cell.Value; val= (typeof val == "string") ? val.replace( /\r\n|\r|\n/g, '<br/>' ) : val; val= (typeof val == "string") ? val.replace( /\t/g, ' ' ) : val; val= (typeof val == "string") ? val.replace(/^\s*(.*?)\s*$/, "$1"): val ; if(val){ record.push("'"+val+"'"); }else { record.push("''"); } } WScript.Echo( record.join(',')+"\n" ); } } }finally{ book.Close(); x1.Quit(); }
PHP (PECL)でEXCEL
変態言語PHPも、COMを使うことができる。
php 5.2xにはfinallyがない。本当の話。"finallyは存在しない"。
あと、日付と数値が全部文字列になった。16.0⇒16, "2008/12/31 00:00:00"⇒"2008/12/31"
内部エンコード使わないから、変なスクリプト言語なのに文字コード気にしなくてイイ。
<?php function getAbsPath ($filename){ $fso = new COM('Scripting.FileSystemObject'); return $fso->GetAbsolutePathName($filename); } $name = getAbsPath("Book1.xls"); $x1 = new COM('Excel.Application'); $book = $x1->Workbooks->Open( $name ); try { foreach ( $book->Worksheets as $sheet ) { foreach ( $sheet->UsedRange->Rows as $row ) { $record = array(); foreach ( $row->Columns as $cell ){ $val = $cell->Value; $val = preg_replace( '/\r\n|\r|\n/', "<br/>", $val ); $val = preg_replace( '/\t/', " ", $val ); $val = trim($val); if($val){ $record[] = "'{$val}'"; }else{ $record[] = "''"; } } echo implode(",", $record). "\n"; } } }catch(Exception $e){ print($e->message); } //php 5.2xにはfinallyがない。 //本当の話。"finallyは存在しない"。 $book->Close(); $x1->Quit();
VbScriptの例
Try..Catch..Finallyがなかったり、可変長配列が無かったり。とても面倒。もう二度とやりたくない。
実際やるなら、.NetFrameWorkのCollectionをCreateObjectで利用するとよさそう。
'EXCEL処理サンプル Option Explicit Function getAbsPath ( filename ) Dim fso Set fso = CreateObject("Scripting.FileSystemObject") getAbsPath=fso.GetAbsolutePathName(filename) End Function Dim name name = getAbsPath("Book1.xls") Dim xl,book Set xl= CreateObject("Excel.Application") Set book = xl.Workbooks.Open(name) 'VBScriptにはTry..Cacth..Finallyが無いみたい。 'Try関数を作って実装するといいそうです。 'http://scripting.cocolog-nifty.com/blog/2006/12/on_error_resume_d841.html Sub Try Dim sheet Dim record() For Each sheet In book.Worksheets Dim row For Each row In sheet.UsedRange.Rows Dim cell,idx reDim record(row.Columns.Count) idx=0 For Each cell in row.Columns Dim val,regx Set regx = new RegExp regx.Global = True val = cell.Value regx.pattern= Chr(10)&Chr(13)&"|"&Chr(10)&"|"&Chr(13)'"\r\n|\r|\n" val = regx.Replace(val,"<br/>") regx.pattern= Chr(9) ' "\t" val = regx.Replace(val," ") val = Trim(val) record(idx) = val idx=idx+1 Next WScript.Echo Join( record, "," ) Next Next End Sub On Error Resume Next Call Try() Sub Catch If Err<>0 Then WScript.Echo Err.Description End If On Error Resume Next End Sub Call Catch() Sub Finally book.Close() xl.Quit() End Sub Call Finally()
pythonでEXCEL処理
文字列エンコードが基本的にキモい。isinstanceもキモい。文字列がstr/unicodeのTYPEなのでありオブジェクトではない。Javaでいうプリミティブ型ようなもの。これがオブジェクト指向にならないし。なんか調べにくい。Python3000ならもっと楽だろうな。
#coding:utf-8 import win32com.client import re def getAbsPath( filename ) : fso = win32com.client.Dispatch('Scripting.FileSystemObject') return fso.GetAbsolutePathName(filename) name = getAbsPath("Book1.xls"); xl = win32com.client.Dispatch("Excel.Application") book = xl.WorkBooks.Open( name ) try: for sheet in book.Worksheets : for row in sheet.UsedRange.Rows: record = [] for cell in row.Columns : val =cell.Value val = re.sub(r'\r\n|\r|\n', "<br/>",val) if val and isinstance(val,basestring) else val val = re.sub(r'\t/g', " ",val) if val and isinstance(val,basestring) else val val = val.strip() if val and isinstance(val,basestring) else val if val: record.append( "'"+unicode(val).encode("cp932",'replace')+"'" ) else: record.append( "''" ) print ",".join( record ) finally: book.Close(); xl.Quit()
言語別の感想
言語 | 感想 | ハマリどころ |
---|---|---|
PHP | 暴走野郎。勝手にゴリゴリ進む。 | 日付・数値セルが文字列になる。他言語はちゃんとFloatやDateになってた。*3 |
JScript | 書きやすい。ループコード汚い | ActiveXオブジェクトはEnumerableを使うのがネック |
Python | 文字列とエンコードがキモい。相変わらずドキュメント稀少 | joinがキモい。内部エンコードに暗黙変換するが、出力は暗黙変換しない。頼んでもいないエンコで容赦なくエラー。win32comのインストールが面倒だった。 |
VbScript | 良くこんなもので仕事できるな | VBと別物がネック。ハッシュ無し。可変長フィールドなし。エラー処理不可。*4 |
ruby | ループが独特。あとは理解しやすい。行儀がいいね。 | 破壊的メソッドと非破壊メソッドではまるかもね。 |
perl | そのうち試す。 | ActivePerlを入れてる人少ない。 |
IronPython | そのうち試す。 | .NetFrameWorkは行儀がよい予感?? |
Java | ソース行方不明 | 相変わらずタイピングの量が鬼。あとデザインパターン知らないと厳しいかも |
EXCELを各種言語で処理利する処理スクリプト。
EXCEL⇒CSVする。EmEditorがあれば、コピペで終わる。だけど、各種言語から扱うことで、
COMの基本とか言語の基本勉強になってちょうどいい。