ページ 11

CSVファイル読み込みについて

Posted: 2012年9月11日(火) 21:46
by テルヒロ
初めて投稿させていただきます。

昨日 龍神録プログラミングの館にて 敵のデータをCSVファイルから読み込むという方法を知りました。
そこで自分なりにCSVファイルを読み込むプログラミングを組んでみたのですが、実行すると初期画面(真っ黒)が表示されアプリケーションが応答なしの状態になってしまいます。 CUIで組み直して実行するとうまくいくのですが…
どこがいけないのか助言をいただけないでしょうか? csvファイルの中身は数字だけで18行38列です。

お見苦しいところもあると思いますがよろしくお願い致します。

OS: Windows7
コンパイラ: visual c++

コード:

/*main.cpp*/

#include <iostream>
#include <DxLib.h>

#include "Load.h"

using namespace std;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){

	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	load_csv* loading = new load_csv;

	loading->object_inf("story.csv");	//ここでCSVファイル読み込み
	
			// while(裏画面を表画面に反映, メッセージ処理, 画面クリア)
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0){
		DrawFormatString( 10, 10, RGB(255,255,255), "[0].cnt = %d", loading->enemy_inf[0].cnt);
		//ためし書き
	}

	delete loading;

	DxLib_End(); // DXライブラリ終了処理
	return 0;
} 

コード:

/*Load.h*/
#pragma once

#include <DxLib.h>
#include <string>
#include <sstream>
#include <fstream>
#include <vector>

#include "struct.h"

using namespace std;

class load_csv{

public:

	enemy_order_t enemy_inf[38];           //csvファイルのデータを詰め込む構造体

	load_csv();									
	vector<string> lead_csv(TCHAR*);			//csvファイルを読み込む
	vector<string> split_line(string, char);	//文字列をコンマで分割する
	double string_to_double(string);			//string型のデータをdouble型に変換する
	vector< vector<double> > create_table(TCHAR*);	//csvファイルのデータをdouble型の2次元配列に格納する
	void object_inf(TCHAR*);					//csvファイルのデータを構造体に代入

};

コード:

/*struct.h*/
#pragma once

struct enemy_order_t{
	//カウンタ、移動パターン、敵の種類
	int cnt,pattern,knd;
	//初期座標と移動スピード
	double x,y,sp;
	//弾幕開始時間、弾幕の種類、色、体力、弾の種類、停滞時間、アイテム(6種類)
	int bltime,blknd,col,hp,blknd2,wait,item_n[6];
};

コード:

/*Load.cpp*/

#include "Load.h"

load_csv::load_csv(){
	memset(enemy_inf, 0, sizeof(enemy_order_t));
}

/*
*	@user
*	機能: csvファイルを読み込む
*	引数: TCHAR*
*	返り値: vector<string>
*/

vector<string> load_csv::lead_csv(TCHAR* filename){

	TCHAR str [256];		//csvファイルから読み取った一行を保持
	vector<string> out;		//返り値
	//ファイルを開く処理
	int ifs = FileRead_open(filename) ;

	if(ifs == NULL){
		printfDx("read error\n");
	}

	//1行ずつcsvファイルを読み込みoutに代入
	while(FileRead_gets(str,256,ifs)){ 
		out.push_back(str);
	}

	return out;
}


/* 
*   @user 
*   機能: 文字列をコンマで分割する
*   引数: string, char
*   返り値: string型のvector
*/

vector<string> load_csv::split_line(string str, char delim){
	vector<string> out;
	stringstream lineStream(str);
	string cell;
	while(getline(lineStream, cell, delim)){
		out.push_back(cell);
	}
	return out;
}


/*
*	@user
*	機能: string型のデータをdouble型に変換する
*	引数: string
*	返り値: double
*/

double load_csv::string_to_double(string str){

	double d;
	stringstream ss;
	ss << str;
	ss >> d;
	return d;
}


/*
*	@user
*	機能: csvファイルのデータをdouble型の2次元配列に格納する
*	引数: TCHAR*
*	返り値: vector< vector<double> >
*/

vector< vector<double> > load_csv::create_table(TCHAR* filename){

	vector< vector<double> > out;	//2次元配列にcsvのデータを格納する 2次元配列(double型)
	vector< vector<string> > x;		//2次元配列にcsvのデータを格納する 2次元配列(string型)
	vector<string> csv;				//コンマで区切られていない状態で、csvデータを保持する 配列
	vector<double> carry;			//csvファイルのデータを1行だけ保持する 配列
	double temp = 0.0;				//string型からdouble型に変換したデータをいったん保持する 変数

	//csvファイルを文字列のまま代入
	csv = lead_csv(filename);			
	
	//2次元配列にcsvファイルのデータが入ったtableを作る(string型)
	for(int i=0; i<csv.size(); i++){
		x.push_back(split_line(csv[i], ','));
	}

	//string型のtableをdouble型に変換して2次元配列detaに格納
	for(int i=0; i<x.size(); i++){
		for(int j=0; j<x[i].size(); j++){
			temp = string_to_double(x[i][j]);
			carry.push_back(temp);
		}
		out.push_back(carry);
		carry.clear();
	}
	return out;
}


/*
*	@user
*	機能: csvファイルのデータを構造体に代入
*	引数: TCHAR*
*	返り値: void
*/

void load_csv::object_inf(TCHAR* filename){

	vector< vector<double> > inf = create_table(filename);

	for(int i=0; i<inf.size(); i++){
		enemy_inf[i].cnt		=inf[i][0];
		enemy_inf[i].pattern	=inf[i][1];
		enemy_inf[i].knd		=inf[i][2];
		enemy_inf[i].x			=inf[i][3];
		enemy_inf[i].y			=inf[i][4];
		enemy_inf[i].sp			=inf[i][5];
		enemy_inf[i].bltime		=inf[i][6];
		enemy_inf[i].blknd		=inf[i][7];
		enemy_inf[i].col		=inf[i][8];
		enemy_inf[i].hp			=inf[i][9];
		enemy_inf[i].blknd2		=inf[i][10];
		enemy_inf[i].wait		=inf[i][11];
		enemy_inf[i].item_n[0]	=inf[i][12];
		enemy_inf[i].item_n[1]	=inf[i][13];
		enemy_inf[i].item_n[2]	=inf[i][14];
		enemy_inf[i].item_n[3]	=inf[i][15];
		enemy_inf[i].item_n[4]	=inf[i][16];
		enemy_inf[i].item_n[5]	=inf[i][17];
	}

}


Re: CSVファイル読み込みについて

Posted: 2012年9月11日(火) 21:59
by nil
ブレークポイントを使用し、問題のある箇所を絞り込むのが確実です。
1.配列などの範囲外にアクセスしていないか
2.無限ループに陥っていないか
を重点的に確認してください。

memset(enemy_inf, 0, sizeof(enemy_order_t));
これでは構造体配列全体の初期化はできていません

あと、ヘッダーにusing namespaceを書くのは止めたほうがいいです。

Re: CSVファイル読み込みについて

Posted: 2012年9月11日(火) 23:04
by テルヒロ
>>涼雅さん

早い返信ありがとうございます。
アドバイスありがとうございます。今まで極小規模の開発しかしていなかったのでブレークポイントを使用するというのに慣れていませんでした・・・
さっそくやってみたところ

コード:

/*Laod.cpp*/
/*
*   @user
*   機能: csvファイルを読み込む
*   引数: TCHAR*
*   返り値: vector<string>
*/
vector<string> load_csv::lead_csv(TCHAR* filename){
 
    TCHAR str [256];        //csvファイルから読み取った一行を保持
    vector<string> out;     //返り値
    //ファイルを開く処理
    int ifs = FileRead_open(filename) ;
 
    if(ifs == NULL){
        printfDx("read error\n");
    }
 
    //1行ずつcsvファイルを読み込みoutに代入
    while(FileRead_gets(str,256,ifs)){ 
        out.push_back(str);
    }
 
    return out;
}

ここの "while(FileRead_gets(str,256,ifs)){ "で抜け出せずにいました・・・

リファレンスを読み直し "while(FileRead_gets(str,256,ifs) != 0){ "にし
csvファイルの最後に改行を加えたところ うまく実行することができました。

しかし
memset(enemy_inf, 0, sizeof(enemy_order_t));
これでは確かに初期化が出来ていませんでした・・・

実行はうまくいきましたが安定性が不安なので初期化はもう一度調べ直してみます。

ありがとうございました。

Re: CSVファイル読み込みについて

Posted: 2012年9月12日(水) 02:57
by かずま
string の vector を作り、
それを 関数の呼び出し元に返却値としてコピーし、
それを string の 2次元 vector にコピーし、
それを関数の呼び出し元に返却値としてコピーし、
さらに、double の 2次元 vector にコピーし、
それから enemy_inf にコピーする。
コピーのしまくりですね。

次のように書けば、すべてのメンバ関数
lead_csv(read_csv ?)、 split_line, string_to_double, create_table, object_inf
は、要らなくなります。

コード:

/* struct.h */
#pragma once
 
struct enemy_order_t {
    int cnt, pattern, knd; // カウンタ、移動パターン、敵の種類
    double x, y, sp;       // 初期座標と移動スピード
    int bltime, blknd;     // 弾幕開始時間、弾幕の種類
    int col, hp, blknd2;   // 色、体力、弾の種類
    int wait, item_n[6];   // 停滞時間、アイテム(6種類)
};

コード:

/* Load.h */
#pragma once
 
#include "struct.h"
 
class load_csv {
public:
    enemy_order_t enemy_inf[38];
    int size;
 
    load_csv(const char *filename);  // constructor
};

コード:

/* Load.cpp */
 
#include "Load.h"
#include <string>
#include <fstream>
#include <sstream>

using namespace std;
 
load_csv::load_csv(const char *filename)
{
    size = 0;
    ifstream ifs(filename);
    if (!ifs) { printfDx("file open error\n"); return; }
    string line;
    int i;
    for (i = 0; i < 38  && getline(ifs, line); i++) {
        istringstream iss(line);
        char comma;
        iss >> enemy_inf[i].cnt     >> comma
            >> enemy_inf[i].pattern >> comma
            >> enemy_inf[i].knd     >> comma
            >> enemy_inf[i].x       >> comma
            >> enemy_inf[i].y       >> comma
            >> enemy_inf[i].sp      >> comma
            >> enemy_inf[i].bltime  >> comma
            >> enemy_inf[i].blknd   >> comma
            >> enemy_inf[i].col     >> comma
            >> enemy_inf[i].hp      >> comma
            >> enemy_inf[i].blknd2  >> comma
            >> enemy_inf[i].wait    >> comma;
        for (int j = 0; j < 6; j++)
            iss >> enemy_inf[i].item_n[j] >> comma;
    }
    size = i;
} 
ところで、なぜ enemy_order_t enemy_inf[38];
ここで vector を使って、vector<enemy_order_t> enemy_inf;
としたほうがいいんじゃないんですか?

Re: CSVファイル読み込みについて

Posted: 2012年9月12日(水) 11:11
by テルヒロ
>>かずまさん

アドバイスありがとうございます。
このように一気にできる方法があったんですね・・・
なぜか一つ一つ変換する方法しか思いつかなくてコピーの多いコードになってしまいました・・・

enemy_order_t enemy_inf[38]; これもよく考えるとわけわかりませんね
確かにvectorを使用して”38”みたいな汎用性のないものは取り除いた方がいいですね
なんで急に配列を使いだしたのか今ではよくわかりません・・・

まだまだ勉強不足です、もっときれいなコードが書けるように頑張ります。

ありがとうございました。