ホーム < ゲームつくろー! < Programming TIPs編

その15 JavaScriptを使ってExcelファイルからCSVを作る


 ゲーム制作ではテーブルが欠かせません。山ほどあるアイテムの攻撃力、防御力、値段などはテーブルで扱うのが理想です。このテーブルを作成する最強のツールは多分Excelでしょう。

 ところがこのExcelさん、そのままプログラムから読み込むのは何とも大変な人だったりします。内部はバイナリファイルですから解析が必要です。折角のテーブルなのにこれでは魅力半減です。

 テーブルとしてもう一つ魅力的なのは昔ながらの「CSV」です。これはコンマ(,)で値を区切っていくという単純な構造です。それゆえにパース(解析)しやすくプログラムとの相性もかなり良い形式です。しかし、これもこれで問題があります。CSV形式を手打ちすると大概どこかで間違います。コンマが抜けていたり、不必要なスペースが入っていたりです。テーブルの形式を人の手で作るのにはちょっと無理があるわけです。

 Excelは最強にテーブルを作り易いが直接読むのが困難、CSVは読み込み易いけどヒューマンエラーが起きやすい。理想は「Excelの表は人が作り、それを読み込んでCSVを作る」です。この「Excelを読み込んでくれる人」が実はいらっしゃいます。それはJavaScript上で動くActiveXオブジェクトである「Excel.Application」です。この人を使うと、びっくりするくらい簡単にExcelファイル内のセルの値を読み込むことが出来てしまいます。

 本章ではExcel.Applicationオブジェクトを使ってExcelファイルからCSVファイルを作成する方法をご紹介します。



@ 一先ずやってみますよ

 細かい説明はさて置き、取り敢えずざざざっとやってみます。

  まずはExcel上に次のようなテーブルを作ってみましょう:

そして「Table.xls」として保存します。私はC:\workフォルダに保存しました。

 次に「createTable.js」という空のテキストファイルをTable.xlsと同じフォルダ内に作りましょう。ファイル名は実は何でも良いのですが、拡張子は.jsにして下さい(JavaScriptファイル)。エディタで作ったテキストファイルを開き、次のプログラムを書き込みます:

var excelApp = WScript.CreateObject("Excel.Application");
var book = excelApp.Workbooks.Open("c:\\work\\Table.xls");
var sheet = book.WorkSheets("Sheet1");

// Excel内を読み込み
var itemName = sheet.Cells(3, 2);
var weaponType = sheet.Cells(3, 3);
var power = sheet.Cells(3, 4);
var deffence = sheet.Cells(3, 5);
var price = sheet.Cells(3, 6);

WScript.Echo(itemName, weaponType, power, deffence, price);

book.Close();
excelApp.Quit();
excelApp = null;

最後に次のようなバッチファイルをテキストで作り「run.bat」として保存しましょう:

cscript createTable.js
pause

はい、これでrun.batをダブルクリックすると次のようなDOS画面が出ます:

読めてます〜〜(^-^)。

という風に実は簡単にExcel上のテーブルが読めてしまいます。

 ちょっと簡単に説明を。WScript.CreateObjectというのはActiveXオブジェクトを作るおまじないです。今回はExcel.Applicationというオブジェクトを作っています。この人は名前の通りExcelを扱ってくれる人です。Excel.Application.WorkBooks.Openメソッドはエクセルファイルを開いてくれます。戻り値はワークブック(book)です。ワークブックの中には複数のワークシートが含まれます。これはExcelを扱った事のある方なら大概わかりますね。名前を指定してワークシートを取得するのがbook.WorkSheets("Sheet1")の部分です。Sheetの中には沢山のセル(Cell)があります。セルはエクセルの最小単位ですね。そのセルにアクセスするのがWorkSheet.Cellsメソッドです。

 アクセスして値を得た後、Workbookを閉じる必要があります。Workbook.Closeメソッドです。これをしないとスクリプトが終わった後もファイルの権限が残ってしまい、本体のExcelファイルを開いても読み込み専用になってしまいます(もしなったらタスクマネージャからEXCEL.exeを終了させます)。最後にExcel.Application自体も終了させて(Quitメソッド)おしまいです。



A CSVファイルとして保存する

 先のJavaScriptは上のようにDOS窓に読んだ結果を出力(Echo)しているだけです。そこで読み込んだ内容をファイルに書き出してみます。これもJavaScript上から行ないますよ。

 簡単な例を挙げます。テキストファイルを作ってそこに文字列を書き込むJavaScriptは次のようになります: 

var fs = WScript.CreateObject("Scripting.FileSystemObject");
fs.CreateTextFile("test.txt");

var file = fs.OpenTextFile("test.txt", 2, 0);
file.Write("Hello !")

file.Close();

ファイルを扱うのはScripting.FileSystemObjectというオブジェクトです。CreateTextFileはその名の通り空のテキストファイルを作ります。作ったテキストファイルをfs.IpenTextFileメソッドで開き(第2引数の「2」が書き込み用を表します)、Writeメソッドに文字列を渡すと書き込み完了です。終わったらCloseメソッドで閉じます。とっても簡単です。

 さ、これがわかれば、後はExcelからセルの情報を読み込みCSVとして整えてファイルに出力するだけです。@で用意したテーブルをCSVに変換するJavaScriptのプログラムは次のようになります:

createCSV.js
var excelApp = WScript.CreateObject("Excel.Application");
var book = excelApp.Workbooks.Open("c:\\work\\Table.xls");
var sheet = book.WorkSheets("Sheet1");

// Excel内をパース
var CSVStr = "";
var r = 0;
while(1) {
    if (sheet.Cells(3 + r, 2).value == null)
        break;
    var ary = new Array;
    var c = 0;
    while(1) {
        var str = sheet.Cells(3 + r, 2 + c);
        if (str.value == null)
            break;
        ary.push(str);
        c++;
    }
    CSVStr += ary.join(",") + "\n";
    r++;
}
book.Close();
excelApp.Quit();
excelApp = null;

// ファイルに書き込み
var fs = WScript.CreateObject("Scripting.FileSystemObject");
fs.CreateTextFile("table.csv");
var file = fs.OpenTextFile("table.csv", 2, 0);
file.Write(CSVStr);
file.Close();

これで次のようなCSVファイルが吐き出されます:

table.csv
アイテム名,武器タイプ,攻撃力,防御力,値段
こんぼう,Weapon,10,0,120
銅の剣,Weapon,30,0,360
鋼の剣,Weapon,70,0,800

上のプログラムは行数、列数に関係なくCSVを吐き出します。

 後はこのCSVファイルをプログラムに読み込んであげればOKです。このような「データビルド」はゲームプログラムで必ず必要となります。JavaScriptはビルド環境を構築する一つの有効な手段ですね。



(2010. 8. 6追記)
B Closeし忘れを無くす

 Excelファイルを開いた状態でスクリプトがエラーを起こすと(開発中はエラー頻発のはず)、WorkbookのCloseメソッドが呼ばれません。すると元のエクセルファイルが開けなくなってしまいます。これを確実に防ぐにはJavaScriptにある「Try〜Catch〜Finally」の仕組みを使います。

table.csv
Try {
    // 監視中〜
}
Catch(e) {
    // スクリプトでエラーが終わったらここにジャンプ
}
Finally {
    // エラーが起きても呼ばれる部分です
}

この仕組みを導入して上のソースを改良したのがこちらです:

createCSV.js
Try {
    var excelApp = WScript.CreateObject("Excel.Application");
    var book = excelApp.Workbooks.Open("c:\\work\\Table.xls");
    var sheet = book.WorkSheets("Sheet1");

    // Excel内をパース
    var CSVStr = "";
    var r = 0;
    while(1) {
        if (sheet.Cells(3 + r, 2).value == null)
            break;
        var ary = new Array;
        var c = 0;
        while(1) {
            var str = sheet.Cells(3 + r, 2 + c);
            if (str.value == null)
                break;
            ary.push(str);
            c++;
        }
        CSVStr += ary.join(",") + "\n";
        r++;
    }
}
Catch(e) {
}
Finally {
    book.Close();
    excelApp.Quit();
    excelApp = null;
}

// ファイルに書き込み
var fs = WScript.CreateObject("Scripting.FileSystemObject");
fs.CreateTextFile("table.csv");
var file = fs.OpenTextFile("table.csv", 2, 0);
file.Write(CSVStr);
file.Close();