はじめに
前回は、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からの翻訳を設計してみます。
次回はブックを更新するコーディングをやります。