sqlite3をDxLibのプログラムに組み込めない

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
tapintohigh

sqlite3をDxLibのプログラムに組み込めない

#1

投稿記事 by tapintohigh » 4年前

ゲームを作っていて、アイテムの管理のところでSQLiteのデータベースを使ってみようと思ったのですが、以前は問題なく動作していたプログラムがSQLiteを用いた関数を入れると、画面には何も表示されなくなり、そのウィンドウはどのボタンを押しても反応しなくなってしまいました。

この問題の理由としてはどのようなものが考えられるのでしょうか。

一応以下にコードを貼らせていただきます。

お時間のある時によろしくお願いします。

<main.cpp>

コード:

#include"DxLib.h"
#include"control.h"

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
	ChangeWindowMode(true);
	SetDrawScreen(DX_SCREEN_BACK);
	DxLib_Init();

	Control control;

	while(1){
		ScreenFlip();
		ProcessMessage();
		ClearDrawScreen();

		control.Update();
		control.Draw();
	}

	DxLib_End();
	return 0;
}
<control.cpp>

コード:

#include"control.h"
#include"DxLib.h"
#include<tchar.h>

static int MARGIN = 10;

Control::Control(){
	scene = new Scene;
	player = new Player;
	field = new Field;
	battle = new Battle;
	itemTable = new ItemTable;
}

void Control::Update(){
	SetDrawArea(MARGIN, MARGIN, MARGIN+550, MARGIN+450);
	player ->Update();
	field ->Update();
	itemTable->process();     //この行を書いて実行すると、画面になにも出力されず、そのウィンドウは固まってしまいます
	if(scene->battle){
		battle ->Update();
	}
}

void Control::Draw(){
	SetDrawArea(MARGIN, MARGIN, MARGIN+550, MARGIN+450);
	field->Draw();
	player->Draw();
	if(scene->battle){
		battle->Draw();
	}
}
<ItemTable.cpp>

コード:

#include"ItemTable.h"
#include"sqlite3.h"
#include"DxLib.h"
#include<fstream>
#include<iostream>

using namespace std;

ItemTable::ItemTable(){
	scene = new Scene;
}

int ItemTable::callBack(void *NotUsed, int argc, char **argv, char **azColName){
	return 0;
}

int ItemTable::process(){
	//データベースファイルの新規作成
	sqlite3 *ItemDB = 0;
	char *errMsg = 0;
	int rc = sqlite3_open("Item.db", &ItemDB);
	if(rc != SQLITE_OK){
		cerr << "Error when the file open" << endl;
		return -1;
	}

	//アイテムテーブルとストックテーブルの作成
	//アイテムテーブル生成SQL
	char ItemTable_Create_SQL[] = "create table ItemTable(ID INTEGER PRIMARY KEY, Name TEXT NOT NULL, Type TEXT NOT NULL, Attack INTEGER, Defence INTEGER, Comment TEXT NOT NULL)";
	//ストックテーブル生成SQL
	char StockTable_Create_SQL[] = "create table StockTable(ID INTEGER PRIMARY KEY, Stock INTEGER NOT NULL)";

	//生成
	sqlite3_exec(ItemDB, ItemTable_Create_SQL, 0, 0, &errMsg);
	sqlite3_exec(ItemDB, StockTable_Create_SQL, 0, 0, &errMsg);

	sqlite3_close(ItemDB);

	//データベースにアイテムを登録
	char Insert_Item_SQL[] = "INSERT INTO ItemTable(ID, Name, Type, Attack, Defence, Price, Comment)"
		"values(%d, '%s', '%s', %d, %d, %d, '%s')";

	fstream ifs("Item.csv");
	if(!ifs.is_open()){
		sqlite3_close(ItemDB);
		return 0;
	}

	char dummy[100], zName[24], zType[10], zComment[128], InsertSQL[512];
	int iID, iAttack, iDefence, iPrice;
	ifs.getline(dummy, 100);	//コメント無視
	ifs.getline(dummy, 100);	//コラム無視
	while(1){
		ifs >> iID >> zName >> zType >> iAttack>> iDefence >> iPrice >> zComment;
		if(ifs.eof()){
			break;
		}
		//データベースにアイテムを登録
		sprintf(InsertSQL, Insert_Item_SQL, iID, zName, zType, iAttack, iDefence, iPrice, zComment);
		sqlite3_exec(ItemDB, InsertSQL, 0, 0, &errMsg);
	}

	return 0;
}

だんごさん
記事: 273
登録日時: 8年前

Re: sqlite3をDxLibのプログラムに組み込めない

#2

投稿記事 by だんごさん » 4年前

あまり詳しくないので的外れかもしれませんが…

ItemTable.cppの37行目でsqlite3_close(ItemDB);で閉じているのに60行目のsqlite3_execでアクセスしようとしているのはどうでしょうか。
 Dango San

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#3

投稿記事 by tapintohigh » 4年前

だんごさん、

返信ありがとうございます。
せっかくお早く返信いただいたのに返せなくて申し訳ないです。

ご指摘の点は確かにおかしいと思ったので、関数の終端に書き直しましたが、やはりプログラムの動作は改善されません。。

ほかにお気づきの点があればよろしくお願いします。

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#4

投稿記事 by tapintohigh » 4年前

すいません、tapintohighです。
追記です。

上記の<ItemTable.cpp>において、53行目から61行目までのwhile文をコメントアウトしたところ、プログラムが正常に動作したので、この中に問題があるようです。適当な処理をご存知のかたがいれば、ご教授のほうよろしくお願いします。

もちろん自分でも引き続き取り組むので、解決し次第また報告させていただきます。

だんごさん
記事: 273
登録日時: 8年前

Re: sqlite3をDxLibのプログラムに組み込めない

#5

投稿記事 by だんごさん » 4年前

C++のファイル系統は触ったことなかったので少し勉強してきました。

55行目のファイルの終わりを確認する部分はfail関数で確認するべきだと思います。
>>演算子で読み込む場合はfail関数で失敗したかが分かるそうです。
どちらにせよeofで確認するのであれば少なくとも54行目の前に来るべきだと思います。
今回はおそらくファイルの読み込みでエラーが発生したのにファイルの末端というわけではないため永遠とループしたのではないかなと思います。
違ってたらすみません…。
 Dango San

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: sqlite3をDxLibのプログラムに組み込めない

#6

投稿記事 by ISLe » 4年前

Item.csvというファイル名からするとカンマ区切りのデータなのでしょうか。
fstreamで読み取るなら空白文字で区切る必要があります。

(VC++2015で)実験してみたらカンマ区切りだとeofにもfailにもならず延々とループしました。


eofはアクセスした際にフラグがセットされるので読み取り操作後でなければいけません。

if (!ifs) break;
と書くとeofとfailのどちらか一方でも真になればbreakします。
特に区別する必要がなければこちらの書き方をお勧めします。

だんごさん
記事: 273
登録日時: 8年前

Re: sqlite3をDxLibのプログラムに組み込めない

#7

投稿記事 by だんごさん » 4年前

ISLe さんが書きました: fstreamで読み取るなら空白文字で区切る必要があります。

eofはアクセスした際にフラグがセットされるので読み取り操作後でなければいけません。

if (!ifs) break;
と書くとeofとfailのどちらか一方でも真になればbreakします。
特に区別する必要がなければこちらの書き方をお勧めします。
なるほど、勉強になりました…
先程の自分の回答はスルーされて結構です。
 Dango San

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#8

投稿記事 by tapintohigh » 4年前

お二方のアドバイスを参考にして53行目から61行目までのwhile文を以下のように書き直したところ、見事正常に動作しました!

私の問題のために時間を割いてくださって大変感謝いたします。
本当に助かりました。ありがとうございました。

コード:

while(1){
	if(!ifs){
		break;
	}

	ifs >> iID >> zName >> zType >> iAttack>> iDefence >> iPrice >> zComment;

	//データベースにアイテムを登録
	sprintf(InsertSQL, Insert_Item_SQL, iID, zName, zType, iAttack, iDefence, iPrice, zComment);
	sqlite3_exec(ItemDB, InsertSQL, 0, 0, &errMsg);
}

ISLe
記事: 2646
登録日時: 9年前
連絡を取る:

Re: sqlite3をDxLibのプログラムに組み込めない

#9

投稿記事 by ISLe » 4年前

正しく読み取れたとしても、最後に余分に不正なレコードを書き込みむコードになっていますよ。

ファイルがカンマ区切りだと、そもそもすべてのフィールドを正しく読み取れません。
2レコード目を読み取る前にfailフラグでbreakしますが
1レコード目で読み取れなかったフィールドは不正な値が書き出されます。

#実験を進めたところ、カンマ区切りでもカンマの前後に空白文字があると無限ループにならない場合がありました。


fstreamで読み取るファイル(Item.csv)はカンマ区切りではなく、空白文字区切りでなければならない。
breakの位置は読み取りの後でなければならない。

コード:

while(1){
    ifs >> iID >> zName >> zType >> iAttack>> iDefence >> iPrice >> zComment;
    if(!ifs){
        break;
    }

    //データベースにアイテムを登録
    sprintf(InsertSQL, Insert_Item_SQL, iID, zName, zType, iAttack, iDefence, iPrice, zComment);
    sqlite3_exec(ItemDB, InsertSQL, 0, 0, &errMsg);
}

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#10

投稿記事 by tapintohigh » 4年前

ISLeさん、

返信をいただいていると思わなかったので、遅れてしまいました。申し訳ないです。

お2人に問題を解決していただいたすぐ後に、自分のもとのプログラムではカンマが検出できていないことに気づき、美しくはないですが、カラム(列名)ごとにgetlineを使って一つ一つ検出していくことでなんとかcsvファイルを読み込むことができました。

そこまでしていただいて本当にありがとうございました。

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: sqlite3をDxLibのプログラムに組み込めない

#11

投稿記事 by YuO » 4年前

ところで,メッセージループ内でCREATE TABLEとINSERTを毎回やっているようですが,これはControlクラス生成時に一回,もっと言えばインストール時に一度行えばよいことではないでしょうか。
おそらく,CREATE TABLEは失敗しているはずですし,INSERTによってデータが繰り返し大量にデータベースに含まれることになるかと思います。

あと,sprintfによるSQL構築は,どうしてもInjectionが気になります。
秘匿データが含まれるわけではないので致命的にはならないでしょうけれど,プリペアードステートメントを使った方が安全ですし効率もよいかと思います。

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#12

投稿記事 by tapintohigh » 4年前

YuOさん、

返信ありがとうございます。

CREATE TABLE と INSERT に関してですが、こちらは自分でもそう思ったので、コーディングしなおしました。

prepared statement について今調べてみたのですが、1つわからない点があります。
以下に現在のコードを貼らせていただくので、それを参照していただくとわかりやすいかと思います。
わからない点というのは、テーブルから値を抽出するときに、146行目のように、コールバック関数を参照しなければいけないと思うのですが、prepared statementだとコールバック関数が使われていません。この場合、どのようにしてテーブルから抽出したデータを受け取ればよいのでしょうか?
(投稿時に貼らせていただいたコードとの変更点は、processの名前をわかりやすさのためにloadItemに変更したこと、そのloadItemをDrawの冒頭(127行目)で実行していること、csvファイルの読み込みかたをgetlineを用いてカンマを1つずつ検出するように変えたことです。)

また話が逸れてしまって申し訳ないのですが、現在つまづいている問題は、Draw関数中において146行目のsqlite3_execでコールバック関数を用いて、ItemListというItemTable型のlistにテーブルから抽出したデータを入れているのですが、151行目のItemList.empty()がtureになってしまうということで、ItemListに何も格納されていないようで、その理由が特定できていない状態です。

大変長くなってしまい、申し訳ないです。
お時間のある時に返信いただければと思います。
よろしくお願いします。

<ItemTable.cpp>

コード:

#include"ItemTable.h"
#include"sqlite3.h"
#include"DxLib.h"
#include<fstream>
#include<sstream>
#include<iostream>
#include<list>

//loadItemを一回だけ実行する
static int itemLoadCount = 0;

ItemTable::ItemTable(){
	scene = new Scene;
}

int ItemTable::itemAllCallback(void *itemInfo, int size, char **data, char **ColName){
	list<ItemTable> *pItemList = (list<ItemTable>*)itemInfo;
	ItemTable itemTable;
	
	itemTable.ID = atoi(data[0]);
	itemTable.Name = data[1];
	itemTable.Type = data[2];
	itemTable.Attack = atoi(data[3]);
	itemTable.Defence = atoi(data[4]);
	itemTable.Price = atoi(data[5]);
	itemTable.Comment = data[6];
	itemTable.Stock = atoi(data[7]);
	
	pItemList->push_back(itemTable);
	return 0;
}

//名前をprocessからloadItemに変更しました
int ItemTable::loadItem(){
	//データベースファイルの新規作成
	sqlite3 *ItemDB = 0;
	char *errMsg = 0;
	int rc = sqlite3_open("Item.db", &ItemDB);
	if(rc != SQLITE_OK){
		cerr << "Error when the file open" << endl;
		return -1;
	}
	
	//アイテムテーブルとストックテーブルの作成
	//アイテムテーブル生成SQL
	char ItemTable_Create_SQL[] = "create table ItemTable(ID INTEGER PRIMARY KEY, Name TEXT NOT NULL, Type TEXT NOT NULL, Attack INTEGER, Defence INTEGER, Comment TEXT NOT NULL)";
	//ストックテーブル生成SQL
	char StockTable_Create_SQL[] = "create table StockTable(ID INTEGER PRIMARY KEY, Stock INTEGER NOT NULL)";

	//生成
	sqlite3_exec(ItemDB, ItemTable_Create_SQL, 0, 0, &errMsg);
	sqlite3_exec(ItemDB, StockTable_Create_SQL, 0, 0, &errMsg);

	//アイテムデータベースにアイテムを登録
	char Insert_Item_SQL[] = "INSERT INTO ItemTable(ID, Name, Type, Attack, Defence, Price, Comment)" "values(%d, '%s', '%s', %d, %d, %d, '%s')";
	//ストックデータベースにアイテムを登録
	char Insert_Stock_SQL[] = "INSERT INTO StockTable(ID, Stock)" "values(%d, %d)";

	fstream ifs("Item.csv");
	if(!ifs){
		cerr << "Error: file not found" << endl;
		sqlite3_close(ItemDB);
		return -1;
	}

	string str, token;
	stringstream id, name, type, attack, defence, price, comment;
	int commentFind = 0;
	int columnFind = 0;
	int commaFind = 0;
	int ID, ATTACK, DEFENCE, PRICE;
	int STOCK = 0;
	char NAME[16], TYPE[8], COMMENT[128], InsertSQL[512], InsertStockSQL[512];
	
	while(getline(ifs, str)){
		
		istringstream stream(str);

		//コメントアウトされた行をスルー
		if((commentFind = str.find("//"))!=str.npos){
			continue;
		}
		//IDから始まるカラムの行をスルー
		if((columnFind = str.find("//"))!=str.npos){
			continue;
		}

		if(getline(stream, token, ',')){
			id << token;
			id >> ID;
		}
		if(getline(stream, token, ',')){
			name << token;
			name >> NAME;
		}
		if(getline(stream, token, ',')){
			type << token;
			type >> TYPE;
		}
		if(getline(stream, token, ',')){
			attack << token;
			attack >> ATTACK;
		}
		if(getline(stream, token, ',')){
			defence << token;
			defence >> DEFENCE;
		}
		if(getline(stream, token, ',')){
			price << token;
			price >> PRICE;
		}
		if(getline(stream, token, ',')){
			comment << token;
			comment >> COMMENT;
		}
		
		sprintf(InsertSQL, Insert_Item_SQL, ID, NAME, TYPE, PRICE, COMMENT);
		sqlite3_exec(ItemDB, InsertSQL, 0, 0, &errMsg);

		sprintf(InsertStockSQL, Insert_Stock_SQL, ID, STOCK);
		sqlite3_exec(ItemDB, InsertStockSQL, 0, 0, &errMsg);
	}

	sqlite3_close(ItemDB);
	return 0;
}

void ItemTable::Draw(){
	//アイテムのロード
	if(itemLoadCount == 0){
		loadItem();
		itemLoadCount += 1;
	}

	//データベースファイルをオープン
	sqlite3 *ItemDB = 0;
	char *errMsg = 0;

	int rc = sqlite3_open("Item.db", &ItemDB);
	if(rc != SQLITE_OK){
		return;
	}
	
	//所持しているアイテムを抽出するSQL
	char item_having_SQL[] = "SELECT ItemTable.Name, ItemTable.Type, ItemTable.Attack, ItemTable.Defence, ItemTable.Price, ItemTable.Comment, StockTable.Stock FROM ItemTable NATURAL INNER JOIN StockTable";
	
	list<ItemTable> ItemList;
	sqlite3_exec(ItemDB, item_having_SQL, itemAllCallback, &ItemList, &errMsg);
	
	//もしlistがemptyであれば、そのことを表示する
	if(ItemList.empty()){
		DrawBox(113, 14, 401, 241, GetColor(255,255,255), false);
		DrawBox(114, 15, 400, 240, GetColor(0,0,0), true);
		DrawFormatString(140, 70, GetColor(255, 255, 255),"ItemList is empty!!");
	}

    //アイテムを表示
    //listのiteratorを利用して、持ち物の場合Stockが1以上のアイテムを検索し、表示していく
	list<ItemTable>::iterator itr;
	for(auto itr = ItemList.begin(); itr != ItemList.end(); itr++){
		if((*itr).Stock > 0){
			strcpy(arrayName,(*itr).Name.c_str());
			DrawBox(113, 14, 401, 241, GetColor(255,255,255), false);
			DrawBox(114, 15, 400, 240, GetColor(0,0,0), true);
			DrawFormatString(140, 40, GetColor(255, 255, 255),"%s", arrayName);
			DrawFormatString(180, 40, GetColor(255, 255, 255),"%d", (*itr).Stock);
		}
	}

	sqlite3_close(ItemDB);
}

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: sqlite3をDxLibのプログラムに組み込めない

#13

投稿記事 by YuO » 4年前

tapintohigh さんが書きました:prepared statement について今調べてみたのですが、1つわからない点があります。
以下に現在のコードを貼らせていただくので、それを参照していただくとわかりやすいかと思います。
わからない点というのは、テーブルから値を抽出するときに、146行目のように、コールバック関数を参照しなければいけないと思うのですが、prepared statementだとコールバック関数が使われていません。この場合、どのようにしてテーブルから抽出したデータを受け取ればよいのでしょうか?
sqlite3_execの説明の先頭に答えがあると思うのですが……。

sqlite3_execは,sqlite3_prepare_v2sqlite3_stepsqlite3_finalizeの簡易ラッパーなのですから,この順序で呼び出せばよいことになります。
値の取得自体はcolumn access functionsを使うと,sqlite3_step関数の説明にあります。
tapintohigh さんが書きました:また話が逸れてしまって申し訳ないのですが、現在つまづいている問題は、Draw関数中において146行目のsqlite3_execでコールバック関数を用いて、ItemListというItemTable型のlistにテーブルから抽出したデータを入れているのですが、151行目のItemList.empty()がtureになってしまうということで、ItemListに何も格納されていないようで、その理由が特定できていない状態です。
まず,エラーのチェックを必ず行うようにして下さい。SQLite3のCインターフェースを利用している場合,まずは戻り値の確認になります。
丁寧に調べていれば,おそらく118行目に相当する行において,エラーが発生しているはずです。
そして,エラーが出たならそれをログに記録するなどして,原因を探る必要があります。
今回の場合,errMsgをログに記録する,標準出力に記録する等が必要になります。
オフトピック
46行目のCREATE TABLE文を見る限り,Priceという列は存在しないので,おそらくそれが原因かと。

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#14

投稿記事 by tapintohigh » 4年前

YuOさん、

返信ありがとうございます。

すみませんでした。Referenceページをしっかり見る癖をつけます。
丁寧に教えていただいてありがとうございました。

エラーのチェックもあまりしたことがないので、一から勉強してみます。

CREATE TABLE文に関するご指摘、とても助かりました。
しかしそこを修正しても、改善されず、ほかに間違っていた箇所を見つけたので、修正しました。
<修正箇所>
84行目: if文中の"//"を"ID"に変更
117行目: sprintfの引数にATTACKとDEFENCEを追加

現在の進捗状況では、Draw関数を実行したときに、以下のようなエラーが出てきてしまいます。
<エラー内容>
ハンドルされていない例外が発生しました: 0xC0000005: 場所 0xcdcdcd00 を読み込み中にアクセス違反が発生しました。
(strlen.asm 81行目)
main_loop:
mov eax,dword ptr [ecx] ; read 4 bytes

いろいろ改善を試みたのですが、このエラーが出続けてしまいます。このエラーはいったいどのようなことが原因で出てしまうのでしょうか?
必要かはわかりませんが、環境はwindows10でvisual studio 10 expressを使用しています。

教えていただいたprepared statementとエラーやログについては、プログラムが正常に動作するようになってから、実装したいと思います。
またお時間のある時にご教授いただければと思います。
よろしくお願いします。

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: sqlite3をDxLibのプログラムに組み込めない

#15

投稿記事 by へにっくす » 4年前

tapintohigh さんが書きました:CREATE TABLE文に関するご指摘、とても助かりました。
しかしそこを修正しても、改善されず、ほかに間違っていた箇所を見つけたので、修正しました。
<修正箇所>
84行目: if文中の"//"を"ID"に変更
117行目: sprintfの引数にATTACKとDEFENCEを追加

現在の進捗状況では、Draw関数を実行したときに、以下のようなエラーが出てきてしまいます。
<エラー内容>
ハンドルされていない例外が発生しました: 0xC0000005: 場所 0xcdcdcd00 を読み込み中にアクセス違反が発生しました。
(strlen.asm 81行目)
main_loop:
mov eax,dword ptr [ecx] ; read 4 bytes
修正したなら修正したコードを載せないと、何も指摘できませんよ。
またエラーが起きたなら、
Visual Studioのデバッガでどこで止まっているか確認する必要があります。
呼び出し履歴があるはずなので、Draw関数のどこで止まっているか確認してください。
written by へにっくす

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#16

投稿記事 by tapintohigh » 4年前

へにっくすさん、

返信ありがとうございます。

失礼しました。
修正後のコードを以下に今一度貼らせていただきます。

また、デバッガを使って調べたところ、148行目のsqlite3_exec(ItemDB, item_having_SQL, itemAllCallback, &ItemList, &errMsg)でエラーが発生していることがわかったので、次にitemAllCallback関数内にブレークポイントを設定して調べてみると、28行目のcommentの部分でエラーが発生しました。

いまだにこのエラーがなぜ起こっているのかわかっていないので、お時間があればよろしくお願いします。

<ItemTable.cpp>

コード:

#pragma comment(lib, "sqlite3.lib")

#include"ItemTable.h"
#include"sqlite3.h"
#include"DxLib.h"
#include<fstream>
#include<sstream>
#include<iostream>
#include<list>

//implement loadItem only once
static int itemLoadCount = 0;

ItemTable::ItemTable(){
	scene = new Scene;
}

int ItemTable::itemAllCallback(void *itemInfo, int size, char *data[], char **ColName){
	list<ItemTable> *pItemList = (list<ItemTable>*)itemInfo;
	ItemTable itemTable;
	
	itemTable.ID = atoi(data[0]);
	itemTable.Name = data[1];
	itemTable.Type = data[2];
	itemTable.Attack = atoi(data[3]);
	itemTable.Defence = atoi(data[4]);
	itemTable.Price = atoi(data[5]);
	itemTable.Comment = data[6];
	itemTable.Stock = atoi(data[7]);
	
	pItemList->push_back(itemTable);
	return 0;
}

int ItemTable::loadItem(){
	//データベースファイルの新規作成
	sqlite3 *ItemDB = 0;
	char *errMsg = 0;
	int rc = sqlite3_open("Item.db", &ItemDB);
	if(rc != SQLITE_OK){
		cerr << "Error when the file open" << endl;
		return -1;
	}
	
	//アイテムテーブルとストックテーブルの作成
	//アイテムテーブル生成SQL
	char *ItemTable_Create_SQL = "CREATE TABLE ItemTable(ID INTEGER NOT NULL, Name TEXT NOT NULL, Type TEXT NOT NULL, Attack INTEGER, Defence INTEGER, Price INTEGER, Comment TEXT NOT NULL)";
	//ストックテーブル生成SQL
	char *StockTable_Create_SQL = "create table StockTable(ID INTEGER, Stock INTEGER)";

	//生成
	sqlite3_exec(ItemDB, ItemTable_Create_SQL, 0, 0, &errMsg);
	sqlite3_exec(ItemDB, StockTable_Create_SQL, 0, 0, &errMsg);

	//アイテムデータベースにアイテムを登録
	char *Insert_Item_SQL = "INSERT INTO ItemTable(ID, Name, Type, Attack, Defence, Price, Comment)" "values(%d, '%s', '%s', %d, %d, %d, '%s')";
	//ストックデータベースにアイテムを登録
	char *Insert_Stock_SQL = "INSERT INTO StockTable(ID, Stock)" "values(%d, %d)";

	fstream ifs("Item.csv");
	if(!ifs){
		cerr << "Error: file not found" << endl;
		sqlite3_close(ItemDB);
		return -1;
	}
	
	string str, token;
	int commentFind = 0;
	int columnFind = 0;
	int commaFind = 0;
	int ID, ATTACK, DEFENCE, PRICE;
	int STOCK = 0;
	char NAME[16], TYPE[8], COMMENT[128], InsertSQL[512], InsertStockSQL[512];
	
	while(getline(ifs, str)){
		stringstream id, name, type, attack, defence, price, comment;
		istringstream stream(str);

		//コメントアウトされた行をスルー
		if((commentFind = str.find("//"))!=str.npos){
			continue;
		}
		//IDから始まるカラムの行をスルー
		if((columnFind = str.find("ID"))!=str.npos){
			continue;
		}

		if(getline(stream, token, ',')){
			id << token;
			id >> ID;
		}
		if(getline(stream, token, ',')){
			name << token;
			name >> NAME;
		}
		if(getline(stream, token, ',')){
			type << token;
			type >> TYPE;
		}
		if(getline(stream, token, ',')){
			attack << token;
			attack >> ATTACK;
		}
		if(getline(stream, token, ',')){
			defence << token;
			defence >> DEFENCE;
		}
		if(getline(stream, token, ',')){
			price << token;
			price >> PRICE;
		}
		if(getline(stream, token, ',')){
			comment << token;
			comment >> COMMENT;
		}
		
		sprintf(InsertSQL, Insert_Item_SQL, ID, NAME, TYPE, ATTACK, DEFENCE, PRICE, COMMENT);
		sqlite3_exec(ItemDB, InsertSQL, 0, 0, &errMsg);

		sprintf(InsertStockSQL, Insert_Stock_SQL, ID, STOCK);
		sqlite3_exec(ItemDB, InsertStockSQL, 0, 0, &errMsg);
	}
	
	sqlite3_close(ItemDB);
	return 0;
}

void ItemTable::Draw(){
	//アイテムのロード
	if(itemLoadCount == 0){
		loadItem();
		itemLoadCount += 1;
	}
	
	//データベースファイルをオープン
	sqlite3 *ItemDB = 0;
	char *errMsg = 0;

	int rc = sqlite3_open("Item.db", &ItemDB);
	if(rc != SQLITE_OK){
		return;
	}
	
	//所持しているアイテムを抽出するSQL
	char item_having_SQL[] = "SELECT ItemTable.Name, ItemTable.Type, ItemTable.Attack, ItemTable.Defence, ItemTable.Price, ItemTable.Comment, Stock.StockTable FROM ItemTable NATURAL INNER JOIN StockTable";
	
	list<ItemTable> ItemList;
	sqlite3_exec(ItemDB, item_having_SQL, itemAllCallback, &ItemList, &errMsg);

	//If list is empty, report it
	if(ItemList.empty()){
		DrawBox(113, 14, 401, 241, GetColor(255,255,255), false);
		DrawBox(114, 15, 400, 240, GetColor(0,0,0), true);
		DrawFormatString(140, 70, GetColor(255, 255, 255),"ItemList is empty!!");
	}
	DrawFormatString(140, 40, GetColor(255, 255, 255),"%d", count);
	
	//アイテムを表示
	//listのiteratorを利用して、持ち物の場合Stockが1以上のアイテムを検索し、表示していく
	list<ItemTable>::iterator itr;
	for(auto itr = ItemList.begin(); itr != ItemList.end(); itr++){
		if((*itr).Stock > 0){
			DrawBox(113, 14, 401, 241, GetColor(255,255,255), false);
			DrawBox(114, 15, 400, 240, GetColor(0,0,0), true);
			DrawFormatString(140, 40, GetColor(255, 255, 255),"%s", (*itr).Name.c_str());
			DrawFormatString(180, 40, GetColor(255, 255, 255),"%d", (*itr).Price);
			//DrawFormatString(180, 40, GetColor(255, 255, 255),"%d", (*itr).Stock);
		}
	}

	sqlite3_close(ItemDB);
}

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: sqlite3をDxLibのプログラムに組み込めない

#17

投稿記事 by YuO » 4年前

tapintohigh さんが書きました:また、デバッガを使って調べたところ、148行目のsqlite3_exec(ItemDB, item_having_SQL, itemAllCallback, &ItemList, &errMsg)でエラーが発生していることがわかったので、次にitemAllCallback関数内にブレークポイントを設定して調べてみると、28行目のcommentの部分でエラーが発生しました。
そもそも,ItemTable::Commentの型は何ですか。
また,「エラーが起きた」時の「data[6]」の値は何ですか。
# 不定なポインタなのか,nullポインタなのか,それとも何らかの文字列を指しているのか,文字列を指しているのであればその値は何か

ところで,デバッガで調べたということは,28行目の時点でのdataやColNameの値をちゃんと調べましたか。
たぶん,想定と異なる値が入っていると思います。
  • インタラクティブなGUI (例えばSQLiteSpyなど) を使って,SQLを実行した時の実際の動作を調べる
    →145行目のSQLを流すと,IDは戻ってこないことがGUIで確かめていればわかる
  • DxLibなどの,SQLiteに関係しない部分を取り除き,単純なコンソールアプリケーションでSQLiteに関わる部分だけを実行し,結果を標準出力やファイルに出力して調査する
    →物事を試すときには単機能な物で試して,その結果を実プロジェクトに取り込む,という方式。色々な機能が関わると,何あった時にそれだけ原因となりうる対象が増えます。
などの,バグつぶしの基本的な手法は知っておいた方がよいと思います。
# 後者は機能追加の基本的な手法ですが。

tapintohigh

Re: sqlite3をDxLibのプログラムに組み込めない

#18

投稿記事 by tapintohigh » 4年前

YuOさん、

返信ありがとうございます。

ご指摘の通りコールバック関数の中の変数の値をチェックしたところ、予期していた値とは異なっており、そもそも145行目のSQL文でIDの値を取得しようとしていなかったことに気づくことができ、予期していた値を無事リストに格納、表示することができました。

今回質問させていただくにつれ、問題が本題からどんどんずれてしまっているにも関わらず、基本事項から丁寧に教えてくださって非常に多くの知らなかったことを学べ、とても助かりました。しっかり勉強して、理解を深めたいと思います。
本当にありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る