Excel VBAをJavaScriptに翻訳 その6



Excel VBAをJavaScriptに翻訳 その6:


はじめに

前回は、VBAの構文解析する前提としてVBAの予約語の定義とあわせて翻訳のサンプルVBAを提示しました.

今回は、VBA翻訳結果のJavaScriptをコーディングします。

このJavaScriptへの翻訳が後続の道しるべとしたいと考えています。


前提

  • OS : Windows7以上
  • PoweShellのターミナルで実行
  • VSCodeでコード編集
  • node.js環境構築済み


ExcelJSのインストール

Excelのブックを読み書きするために、npmからExcelJSをローカルにインストールします。

npm install --save exceljs 


JavaScriptへの翻訳

ExcelJSの特徴として読込が非同期のため考慮が必要です。

// read from a file 
var workbook = new Excel.Workbook(); 
workbook.xlsx.readFile(filename) 
    .then(function() { 
        // use workbook 
    }); 
またシートのセルはリッチテキストとして取得可能で、内容はJSON形式で値を得ることができます。

前回準備したExcelのブックのセル(A1)の取得結果が以下になります。

[ { text: '徳川 家康(とくがわ いえやす、旧字体: 德川家康)または' }, 
  { font: 
     { size: 11, 
       color: [Object], 
       name: 'MS Pゴシック', 
       family: 3, 
       charset: 128, 
       scheme: 'minor' }, 
    text: '松平 元康' }, 
  { font: 
     { size: 11, 
       name: 'MS Pゴシック', 
       family: 3, 
       charset: 128, 
       scheme: 'minor' }, 
    text: '(' }, 
  { font: 
     { size: 11, 
       color: [Object], 
       name: 'MS Pゴシック', 
       family: 3, 
       charset: 128, 
       scheme: 'minor' }, 
    text: 'まつだいら もとやす' }, 
  { font: 
     { size: 11, 
       name: 'MS Pゴシック', 
       family: 3, 
       charset: 128, 
       scheme: 'minor' }, 
    text: ')' }, 
  { font: 
     { size: 11, 
       color: [Object], 
       name: 'MS Pゴシック', 
       family: 2, 
       scheme: 'minor' }, 
    text: 'は、戦国時代から安土桃山時代にかけての武将・戦国大名[1]。江戸幕府の初代征夷大将軍[1]。三英傑の一人。「海道一の' }, 
  { font: 
     { size: 11, 
       color: [Object], 
       name: 'MS Pゴシック', 
       family: 3, 
       charset: 128, 
       scheme: 'minor' }, 
    text: '弓取り' }, 
  { font: 
     { size: 11, 
       color: [Object], 
       name: 'MS Pゴシック', 
       family: 2, 
       scheme: 'minor' }, 
    text: '」の異名を持つ。' } ] 
colorはobjectになっています。

objectは { key : key, val: val } の 構造になっています。

color : [Object] 
この文字色を判断する関数を以下のように定義します。

/* 
 *  Color フォント属性の設定色を返却 
 *  @PARAM {any}  : obj - font object 
 *  @Return {any} : color - color 
 */ 
function Color(obj) { 
    if (typeof obj == 'object') { 
        var color = ''; 
        for (key in obj) { 
            if (key == 'color') { 
                for (val in obj[key]) { 
                    if (val == 'argb') { 
                        color = obj[key][val]; 
                    } 
                } 
            } 
        } 
        return color; 
    } else { 
        return obj; 
    } 
} 
読み取りが非同期のため Promise を使って制御します。

関数の制御は以下の構造になります。

function 関数名(引数1, 引数2) { 
    return new Promise(function(resolve) { 
        // asynchronous : 非同期処理 
        wb.xlsx.readFile(filePath).then(function() { 
            let sh = wb.getWorksheet("Sheet1"); 
            処理.... 
            ret = 処理結果; 
            resolve(ret); 
        }); 
    }); 
} 
 
function ラッパー関数(引数1, 引数2) { 
    return 関数名(引数1, 引数2).then(function(val) { 
      return val; 
    }); 
} 
 
Promise の関数を呼び出すラッパー関数に引数を与え、処理結果を取得します。


AnsColor : 問題取得関数

文字色が赤の文字を'□'に変換する関数を作成します。

AnsColorがラッパー関数で、GetProblemがPromise関数になります。

/* 
 *  GetAns 問題取得 
 *  @PARAM {any}  : adrs - Cell Address 
 *  @PARAM {any}  : cls  - font Color 
 *  @Return {any} : resolve - Problem 
 */ 
function GetProblem(adrs, clr) { 
    return new Promise(function(resolve) { 
        // asynchronous : 非同期 
        wb.xlsx.readFile(filePath).then(function() { 
            let sh = wb.getWorksheet("Sheet1"); 
            let cell = sh.getCell(adrs).value; 
            let buf = ''; 
            for (i = 0 ; i < cell.richText.length ; i++) { 
                if (Color(cell.richText[i].font) == clr) { 
                    for ( j = 0 ; j < cell.richText[i].text.length ; j++) { 
                        buf = buf +  '□'; 
                    } 
                } else { 
                    buf = buf +  cell.richText[i].text; 
                } 
            } 
            resolve(buf); 
        }); 
    }); 
} 
 
function AnsColor(adrs, clr) { 
    return GetProblem(adrs, clr).then(function(val) { 
      return val; 
    }); 
} 
 


AnzColor : 解答取得

文字色が赤の文字列を取り出す関数を作成します。

AnzColorがラッパー関数で、GetAnsがPromise関数になります。

/* 
 *  GetAns 解答取得 
 *  @PARAM {any}  : adrs - Cell Address 
 *  @PARAM {any}  : cls  - font Color 
 *  @Return {any} : resolve - Answer  
 */ 
function GetAns(adrs, clr) { 
    return new Promise(function(resolve) { 
        // asynchronous : 非同期 
        wb.xlsx.readFile(filePath).then(function() { 
            let sh = wb.getWorksheet("Sheet1"); 
            let cell = sh.getCell(adrs).value; 
            let buf = ''; 
            for (i = 0 ; i < cell.richText.length ; i++) { 
                if (Color(cell.richText[i].font) == clr) { 
                    buf = buf + "," + cell.richText[i].text; 
                } 
            } 
            resolve(Mid(buf,2)); 
        }); 
    }); 
} 
 
/* 
 *  AnzColor 文字列より指定色の文字列を取得 
 *  @PARAM {any}  : adrs - Cell Address 
 *  @PARAM {any}  : cls  - font Color 
 *  @Return {any} : val - Answer  
 */ 
function AnzColor(adrs, clr) { 
    return GetAns(adrs, clr).then(function(val) { 
      return val; 
    }); 
} 


ファイルヘッダーの宣言と他の関数

関数で共通で使用する変数を宣言します。

Assert/AssertN/Left/Right/Mid関数も定義します。

const Excel = require('exceljs'); 
let wb = new Excel.Workbook(); 
const path = require('path'); 
let filePath = path.resolve(__dirname,'temp.xlsx'); 


tomexcel.js

上記の全ソースです。

const Excel = require('exceljs'); 
let wb = new Excel.Workbook(); 
const path = require('path'); 
let filePath = path.resolve(__dirname,'temp.xlsx'); 
 
/* 
 * Assert 引数有無判定 
 * @PARAM {any} : a - Message 
 * @PARAM {any} : b - Character String 
 */ 
function Assert(a, b) { 
    if (!b) { console.log(a + " 引数がありません"); return true; } else { return false; }    
} 
 
/* 
 * AssertN 引数数値判定 
 * @PARAM {any} : a - Message 
 * @PARAM {any} : b - Numeric Value 
 */ 
function AssertN(a, b) {  
    if (Assert(a, b)) {return true;} if (isNaN(b)) { console.log(a + " 数値ではありません"); return true;  
    } else { return false; } 
} 
 
/* 
 * Left 
 * @PARAM {any} : str - Character String 
 * @PARAM {any} : size - Character Length 
 */ 
function Left(str, size) { 
    if (Assert("Left " + "str", str)) {return Err;} 
    if (AssertN("Left " + "size", size)) {return Err;} 
    let len = (str.length < size) ? str.length : size; 
    return str.substring(0, len); 
} 
 
/* Right 
 * @PARAM {any} : str - Character String 
 * @PARAM {any} : size - Character Length 
 */ 
function Right(str, size) { 
    if (Assert("Right " + "str", str)) {return Err;} 
    if (AssertN("Right " + "size", size)) {return Err;} 
    let len = (str.length < size) ? size : str.length; 
    return str.substr(len - size, size); 
} 
 
/* Mid 
 * @PARAM {any} : str - Character String 
 * @PARAM {any} : pos - Start Position 
 * @PARAM {any} : size - Character Length 
 */ 
function Mid(str, pos, size) { 
    if (Assert("Mid " + "str", str)) {return Err;} 
    if (AssertN("Mid " + "pos", pos)) {return Err;} 
    // size指定あり 
    if(size) {if (AssertN("Mid " + "size", size)) {return Err;}}         
    let len = size + 1 || str.length - pos + 1; 
    return str.substring(pos - 1, len + 1); 
} 
 
/* 
 *  Color フォント属性の設定色を返却 
 *  @PARAM {any}  : obj - font object 
 *  @Return {any} : color - color 
 */ 
function Color(obj) { 
    if (typeof obj == 'object') { 
        let color = ''; 
        for (key in obj) { 
            if (key == 'color') { 
                for (val in obj[key]) { 
                    if (val == 'argb') { 
                        color = obj[key][val]; 
                    } 
                } 
            } 
        } 
        return color; 
    } else { 
        return obj; 
    } 
} 
 
/* 
 *  GetAns 問題取得 
 *  @PARAM {any}  : adrs - Cell Address 
 *  @PARAM {any}  : cls  - font Color 
 *  @Return {any} : resolve - Problem 
 */ 
function GetProblem(adrs, clr) { 
    return new Promise(function(resolve) { 
        // asynchronous : 非同期 
        wb.xlsx.readFile(filePath).then(function() { 
            let sh = wb.getWorksheet("Sheet1"); 
            let cell = sh.getCell(adrs).value; 
            let buf = ''; 
            for (i = 0 ; i < cell.richText.length ; i++) { 
                if (Color(cell.richText[i].font) == clr) { 
                    for ( j = 0 ; j < cell.richText[i].text.length ; j++) { 
                        buf = buf +  '□'; 
                    } 
                } else { 
                    buf = buf +  cell.richText[i].text; 
                } 
            } 
            resolve(buf); 
        }); 
    }); 
} 
 
 
function AnsColor(adrs, clr) { 
    return GetProblem(adrs, clr).then(function(val) { 
      return val; 
    }); 
} 
 
/* 
 *  GetAns 解答取得 
 *  @PARAM {any}  : adrs - Cell Address 
 *  @PARAM {any}  : cls  - font Color 
 *  @Return {any} : resolve - Answer  
 */ 
function GetAns(adrs, clr) { 
    return new Promise(function(resolve) { 
        // asynchronous : 非同期 
        wb.xlsx.readFile(filePath).then(function() { 
            let sh = wb.getWorksheet("Sheet1"); 
            let cell = sh.getCell(adrs).value; 
            let buf = ''; 
            for (i = 0 ; i < cell.richText.length ; i++) { 
                if (Color(cell.richText[i].font) == clr) { 
                    buf = buf + "," + cell.richText[i].text; 
                } 
            } 
            resolve(Mid(buf,2)); 
        }); 
    }); 
} 
 
/* 
 *  AnzColor 文字列より指定色の文字列を取得 
 *  @PARAM {any}  : adrs - Cell Address 
 *  @PARAM {any}  : cls  - font Color 
 *  @Return {any} : val - Answer  
 */ 
function AnzColor(adrs, clr) { 
    return GetAns(adrs, clr).then(function(val) { 
      return val; 
    }); 
} 
 
module.exports = {  
    AnsColor: AnsColor, 
    AnzColor: AnzColor 
};  
 


runexcel.js

作成した関数を実行する処理を作成します。

if( process.argv[2] == undefined || process.argv[3] == undefined ) { 
    console.log( '引数を指定してください!' ); 
    return; 
}; 
 
let f = require('./tomexcel.js'); 
f.AnsColor(process.argv[2], process.argv[3]).then(function(val) { 
    console.log(val); 
}); 
 
f.AnzColor(process.argv[2], process.argv[3]).then(function(val) { 
    console.log(val); 
}); 


実行結果

runexcel.jsの引数にセル(A1)と赤(FFFF0000)を指定します。

PS C:\~\tom> node runexcel.js 'A1' 'FFFF0000' 
徳川 家康(とくがわ いえやす、旧字体: 德川家康)または□□□□□(□□□□□□□□□□)は、戦国時代から安土桃山時代にかけての武将・戦国大名[1]。江戸幕府の初代征夷 
大将軍[1]。三英傑の一人。「海道一の□□□」の異名を持つ。 
松平 元康,まつだいら もとやす,弓取り 
PS C:\~\tom> 


まとめ

ExcelJSを使用してExcelのブックを読込んで結果を取得するJavaScriptをコーディングしました。

(試験が不十分のためバグがありあそうです)

なんとなく翻訳結果を作成してみました。これに至るようにVBAからの翻訳を設計してみます。

次回はブックを更新するコーディングをやります。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)