それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

EXCELを扱って言語比較してみる。js,py,rb,vbs,php

仕事で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
  1. セル内部改行は<br>に置換。
  2. 空白はTrim。
  3. タブはスペースに置換。

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の基本とか言語の基本勉強になってちょうどいい。

エディタにコピペするとセル内改行が!!!

セル内で改行は便利だけど、テキストエディタと相性が悪い。改行\r\nを<br/>に変換したい。


ファイルIOと、パス、正規表現、そしてオブジェクト扱い。基本的要素が詰まっている、
基本入出力ルーチン比較になる。


結論。JScript/rubyがすごくいい。

*1:ライブラリではないけど、ActiveXインスタンス化ってことで

*2:ライブラリでないけど、CreateObjectでインスタンス化ってことで

*3:正確にはミリ秒まで反映した文字列。小数点まで配慮した数値。PHPは容赦なく表示形式と同じ文字列になってて驚いた。

*4:2009-04-08追記:VBScriptはScripting.Dictionary オブジェクトを使うのが正統っぽい。CreateObject("Scripting.Dictionary ");

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy