gametools.networkを利用する API JSON Excelとの連携 C++ Builder CE登場
の続編です。アプリを作ります。結果はExcelのファイルに追記するので、まずExcelのファイルのハンドリングをC++ Builder CEから行うやり方を紹介します。まずヘッダーは、
#include <System.Win.ComObj.hpp> // EXCEL処理用
が必要です。初期化は、ヘッダを見ればお分かりのようにComでやりとりをしますから。
Variant ExcelApp = CreateOleObject("Excel.Application");
Variantという変数の型は見慣れないと思いますが、宣言時には具体的な型が決められず、実行時に決まるという変化する型でVariantです。この型の変数がふんだんに出てきます。結果の1行を既存のExcelのファイルに追加する関数は、
void __fastcall TForm1::Excel_addRow(WideString srcFile, WideString dstFile)
{
bool closeExcel = false;
Variant range2;
Variant ExcelApp = CreateOleObject("Excel.Application");
ExcelApp.OlePropertySet("Visible", true); // Excel not shown
ExcelApp.OlePropertySet("DisplayAlerts", false); // No dialog for overwrite
xls_books = ExcelApp.OlePropertyGet("Workbooks");
xls_abook = xls_books.OleFunction("Open", srcFile);
// 最初のシート選択
xls_abook.OlePropertyGet("Sheets", 1).OleProcedure("Select");
xls_asheet = xls_abook.OlePropertyGet("ActiveSheet");
//
range2 = xls_asheet.OlePropertyGet("UsedRange");
linecount = range2.OlePropertyGet("Rows").OlePropertyGet("Count");
columncount = range2.OlePropertyGet("Columns").OlePropertyGet("Count");
// セルA5のデータをAnsiString Data1に読み込む
// C++Builder 2007では、range = worksheet.OlePropertyGet("Cells","A5"); だったところ。
//range = worksheet.OlePropertyGet("Cells").OlePropertyGet("Item", 5,1);
//Data1 = range.OlePropertyGet("Value");
// dump item names from A3->G3
String items;
String res;
int i = 1;
String temp;
for(;;){
temp = GetItemNames(i);
if( temp == "" )
break;
targets->Add(temp);
i++;
}
GetPersonalStats();
BasicStats->Checked = true;
GetWeaponStats();
WeaponStats->Checked = true;
prepsearch();
for( int i = 0 ; i < targets->Count ; i++ ){
items += targets->Strings[i];
items += " ";
results->Add(searchit(targets->Strings[i]));
}
Memo1->Lines->Add(items);
for( int i = 0 ; i < results->Count ; i++ ){
res += results->Strings[i];
res += " ";
}
Memo1->Lines->Add(res);
//ShowMessage("huh?");
// put cells
for( int i = 0 ; i < results->Count ; i++ ){
putcell(i);
}
//ShowMessage("huh?");
//xls_auxbook.OleFunction("Close");
xls_abook.OleFunction("Save");
closeExcel = true;
ExcelApp.Exec(Procedure("Quit"));
}
この関数で呼び出しているGetItemNames(i)の実体は、
String GetItemNames(int k )
{
Variant range;
range = xls_asheet.OlePropertyGet("Cells").OlePropertyGet("Item", 3,k);
//AnsiString retvalue = range.OlePropertyGet("Value");
//if( retvalue == "" )
// ShowMessage("empty string");
return range.OlePropertyGet("Value");
}
追記するExcelファイルの見出し行(3行目)の左からk番目の項目名をまずcell位置を得て、そのセルの中身(Value)を得て、それを関数の戻り値としてStringで返しています。具体的には、

の”date”,”timePlayed”,”kills”,”deaths”,”killDeath”までが基本statsで、以下”Gewehr 98 Sniper”,”Tripwire Bomb –HE”が着目する武器の指定です。これをglobal領域に宣言した変数
TStringList* targets = new TStringList;
TStringList* results = new TStringList;
bool there = false;
int linecount,columncount;
Variant xls_books;
Variant xls_abook;
Variant xls_sheets;
Variant xls_asheet;
に拾っておきます。GetPersonalStats()は、
void GetPersonalStats()
{
TMemoryStream* ms = new TMemoryStream();
TStringList* atlist = new TStringList();
TIdHTTP* kick = Form1->IdHTTP1;
UnicodeString aurl = TIdURI::URLEncode(
"https://api.gametools.network/bf1/stats/?name=docnao");
int ret = 0;
Form1->Memo1->Lines->Clear();
Form1->Label1->Caption = aurl;
Form1->IdHTTP1->Request->CustomHeaders->Clear(); // for re-use
Form1->IdHTTP1->Request->CustomHeaders->FoldLines = false;
Form1->IdHTTP1->Request->CustomHeaders->Add("accept:application/json");
jsonorig = "";
try {
kick->Get(url,ms);
}
catch(const Exception& e)
{
ShowMessage(e.ClassName());
return;
//printf("Caught C++ Exception: %s :\n", e.msg());
}
ms->Position = 0;
atlist->LoadFromStream(ms, TEncoding::UTF8);
jsonorig="";
//for( int i = 0 ; i < Memo1->Lines->Count ; i++ )
for( int i = 0 ; i < atlist->Count ; i++ ){
jsonorig += atlist->Strings[i];
//Memo1->Lines->Add(atlist->Strings[i]);
}
}
GetWeaponStats()は、
void GetWeaponStats()
{
TMemoryStream* ms = new TMemoryStream();
TStringList* atlist = new TStringList();
TIdHTTP* kick = Form1->IdHTTP1;
//Form1->Memo1->Lines->Clear();
Form1->Label1->Caption = wurl;
Form1->IdHTTP1->Request->CustomHeaders->Clear(); // for re-use
Form1->IdHTTP1->Request->CustomHeaders->FoldLines = false;
Form1->IdHTTP1->Request->CustomHeaders->Add("accept:application/json");
jsonorig2 = "";
try {
kick->Get(wurl,ms);
}
catch(const Exception& e)
{
ShowMessage(e.ClassName());
return;
//printf("Caught C++ Exception: %s :\n", e.msg());
}
ms->Position = 0;
atlist->LoadFromStream(ms, TEncoding::UTF8);
jsonorig2="";
//for( int i = 0 ; i < Memo1->Lines->Count ; i++ )
for( int i = 0 ; i < atlist->Count ; i++ ){
jsonorig2 += atlist->Strings[i];
//Memo1->Lines->Add(atlist->Strings[i]);
}
Form1->IdHTTP1->Disconnect();
}
です。前の記事のcurlで得たのと同様ですね。具体的な検索は、
void prepsearch()
{
LStringReader = new TStringReader(jsonorig);
LJsonTextReader = new TJsonTextReader(LStringReader);
LIterator = new TJSONIterator(LJsonTextReader);
LStringReaderw = new TStringReader(jsonorig2);
LJsonTextReaderw = new TJsonTextReader(LStringReaderw);
LIteratorw = new TJSONIterator(LJsonTextReaderw);
}
String searchit(String target)
{
if( target == "date" )
return Date();
if(LIterator->Find(target)){
if( target == "timePlayed" )
return LIterator->AsString;
else if( target == "killDeath" )
return FloatToStr(LIterator->AsDouble);
else
return IntToStr(LIterator->AsInteger);
}
for( int i = 0 ; i < 247 ; i++ ){
String wepname;
if( LIteratorw->Find("weapons[" + IntToStr(i) + "]" + ".weaponName"))
wepname = LIteratorw->AsString;
if( wepname == target ){
int kills;
if(LIteratorw->Find("weapons[" + IntToStr(i) + "]" + ".kills")){
//ShowMessage("found "+ IntToStr(i) + " " + wepname + LIteratorw->AsString);
kills = LIteratorw->AsInteger;
}
else
ShowMessage("can't pick up "+ wepname + "kills");
return IntToStr(kills);
} // found
}
return "";
}
見ればお分かりかもしれませんが、いわゆる”バカサーチ”で探しています。相手がソートされていないので、しょうが無いですね。武器種類は300以下ですから、それほど時間はかかりません。今取ってきた結果は、

こんな風に得られます。TlistViewでなくExcelファイルに追記すると、簡易DBとして使えるので便利です。とはいえgametools.networkがいつまで稼働してるかは分かりませんね。

アプリの画面の終了時は上図のようになってます。7秒程度でデータを取り込めています。武器名を変更すればそれに対応したstatsが得られますが、途中結果は更新されませんね。あくまで最終行だけですね。ま、時々stats得ています。C++ BuilderからExcelのファイルの読み書きはそれほどめんどくさくはないので、やってみるのが吉です。慣れるまではコツが要りますがね。
コメント