11章のcsvデータの読み込みについて

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

11章のcsvデータの読み込みについて

#1

投稿記事 by sql » 6年前

作っている作品でcsvデータを読み込みたくてここの11章を参考にしているのですが、理解に苦しんでいます。
一応コード(コピペ)

コード:

//敵の出現情報をエクセルから読み込んで格納する関数
void load_story(){
        int n,num,i,fp;
        char fname[32]={"../dat/csv/storyH0.csv"};
        int input[64];
        char inputc[64];

        fp = FileRead_open(fname);//ファイル読み込み
        if(fp == NULL){
                printfDx("read error\n");
                return;
        }
        for(i=0;i<2;i++)//最初の2行読み飛ばす
                while(FileRead_getc(fp)!='\n');

        n=0 , num=0;
        while(1){
                for(i=0;i<64;i++){
                        inputc[i]=input[i]=FileRead_getc(fp);//1文字取得する
                        if(inputc[i]=='/'){//スラッシュがあれば
                                while(FileRead_getc(fp)!='\n');//改行までループ
                                i=-1;//カウンタを最初に戻して
                                continue;
                        }
                        if(input[i]==',' || input[i]=='\n'){//カンマか改行なら
                                inputc[i]='\0';//そこまでを文字列とし
                                break;
                        }
                        if(input[i]==EOF){//ファイルの終わりなら
                                goto EXFILE;//終了
                        }
                }
                switch(num){
                        case 0: enemy_order[n].cnt      =atoi(inputc);break;
                        case 1: enemy_order[n].pattern  =atoi(inputc);break;
                        case 2: enemy_order[n].knd      =atoi(inputc);break;
                        case 3: enemy_order[n].x        =atof(inputc);break;
                        case 4: enemy_order[n].y        =atof(inputc);break;
                        case 5: enemy_order[n].sp       =atof(inputc);break;
                        case 6: enemy_order[n].bltime   =atoi(inputc);break;
                        case 7: enemy_order[n].blknd    =atoi(inputc);break;
                        case 8: enemy_order[n].col      =atoi(inputc);break;
                        case 9: enemy_order[n].hp       =atoi(inputc);break;
                        case 10:enemy_order[n].blknd2   =atoi(inputc);break;
                        case 11:enemy_order[n].wait     =atoi(inputc);break;
                        case 12:enemy_order[n].item_n[0]=atoi(inputc);break;
                        case 13:enemy_order[n].item_n[1]=atoi(inputc);break;
                        case 14:enemy_order[n].item_n[2]=atoi(inputc);break;
                        case 15:enemy_order[n].item_n[3]=atoi(inputc);break;
                        case 16:enemy_order[n].item_n[4]=atoi(inputc);break;
                        case 17:enemy_order[n].item_n[5]=atoi(inputc);break;
                }
                num++;
                if(num==18){
                        num=0;
                        n++;
                }
        }
EXFILE:
        FileRead_close(fp);
}
まず、

コード:

 for(i=0;i<2;i++)//最初の2行読み飛ばす
                while(FileRead_getc(fp)!='\n');
2行読み飛ばすというところがわかりません。これでどうして「2行読み飛ばす」のでしょうか?改行はどの瞬間にしているのでしょうか?
次に

コード:

 while(1){
                for(i=0;i<64;i++){
                        inputc[i]=input[i]=FileRead_getc(fp);//1文字取得する
                        if(inputc[i]=='/'){//スラッシュがあれば
                                while(FileRead_getc(fp)!='\n');//改行までループ
                                i=-1;//カウンタを最初に戻して
                                continue;
                        }
                        if(input[i]==',' || input[i]=='\n'){//カンマか改行なら
                                inputc[i]='\0';//そこまでを文字列とし
                                break;
                        }
                        if(input[i]==EOF){//ファイルの終わりなら
                                goto EXFILE;//終了
                        }
                }
                switch(num){
                        case 0: enemy_order[n].cnt      =atoi(inputc);break;
                        case 1: enemy_order[n].pattern  =atoi(inputc);break;
                        case 2: enemy_order[n].knd      =atoi(inputc);break;
                        case 3: enemy_order[n].x        =atof(inputc);break;
                        case 4: enemy_order[n].y        =atof(inputc);break;
                        case 5: enemy_order[n].sp       =atof(inputc);break;
                        case 6: enemy_order[n].bltime   =atoi(inputc);break;
                        case 7: enemy_order[n].blknd    =atoi(inputc);break;
                        case 8: enemy_order[n].col      =atoi(inputc);break;
                        case 9: enemy_order[n].hp       =atoi(inputc);break;
                        case 10:enemy_order[n].blknd2   =atoi(inputc);break;
                        case 11:enemy_order[n].wait     =atoi(inputc);break;
                        case 12:enemy_order[n].item_n[0]=atoi(inputc);break;
                        case 13:enemy_order[n].item_n[1]=atoi(inputc);break;
                        case 14:enemy_order[n].item_n[2]=atoi(inputc);break;
                        case 15:enemy_order[n].item_n[3]=atoi(inputc);break;
                        case 16:enemy_order[n].item_n[4]=atoi(inputc);break;
                        case 17:enemy_order[n].item_n[5]=atoi(inputc);break;
                }
                num++;
                if(num==18){
                        num=0;
                        n++;
                }
        }
for文の64の意味がわかりません、なぜ64なのでしょうか(メモリ容量なので別に64である必要はないのでしょうか?)?、次に「スラッシュがあれば」とありますが、csvファイルを開いてみましたが、最初の2行しかありません。1つ前の質問でのコードですでに2行読み飛ばしているのならこのコードは必要なのでしょうか?次の質問なのですが以下のようなファイルがあった場合、

コード:

10,111,1,1
1,100,20,3
上のコードの「カンマか改行なら」の部分なのですが(9行目)、「10,」とカンマがあるので10が文字列としてinputcに格納され、breakによりfor文から抜けて、下のswitch文で整数に変換されて格納される、というのはわかるのですが、次のfor文の中では「10,」が抜けた

コード:

111,1,1
1,100,20,3
のような状態になってinputcへの格納が始まるのでしょうか?また、「改行であれば」とありますがどのような場合が「改行」にあたるのでしょうか?自分の例示したデータの「3」の後は、その行ではずっと空白なので、これを「改行」と言うのでしょうか?

下線&太字の文章が質問です。多いですが、困っているので、ご教授お願いします。

初級者
記事: 200
登録日時: 9年前

Re: 11章のcsvデータの読み込みについて

#2

投稿記事 by 初級者 » 6年前

改行コードを検知するまで何もしない、
を2回繰り返す
= 2行読み飛ばす
です。

アバター
みけCAT
記事: 6275
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: 11章のcsvデータの読み込みについて

#3

投稿記事 by みけCAT » 6年前

sql さんが書きました:for文の64の意味がわかりません、なぜ64なのでしょうか(メモリ容量なので別に64である必要はないのでしょうか?)?
むしろ64ではダメだと思います。
inputおよびinputcの要素数が64なので、64文字いっぱい読み込んでしまうと'\0'が格納されず、誤動作の原因になります。
64ではなく、sizeof(inputc)/sizeof(inputc[0])-1とするのがいいと思います。
sql さんが書きました:「スラッシュがあれば」とありますが、csvファイルを開いてみましたが、最初の2行しかありません。1つ前の質問でのコードですでに2行読み飛ばしているのならこのコードは必要なのでしょうか?
今の入力ではたまたまコメント行がありませんが、コメント行を書ける仕様にするために必要なのだと思います。
sql さんが書きました:次のfor文の中では「10,」が抜けた(略)のような状態になってinputcへの格納が始まるのでしょうか?
はい。
sql さんが書きました:また、「改行であれば」とありますがどのような場合が「改行」にあたるのでしょうか?自分の例示したデータの「3」の後は、その行ではずっと空白なので、これを「改行」と言うのでしょうか?
ソースコードを読むと、読み込んだ文字コードがLFである場合が「改行」にあたるようです。
おそらく、テキストファイルの最後の行の最後に改行コードを入れないと、その行の最後の要素は読み込まれないと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

sql

Re: 11章のcsvデータの読み込みについて

#4

投稿記事 by sql » 6年前

返信ありがとうございます。おかげで理解できました!
付加質問なのですが、

コード:

123
1
21
のようなテキストファイルを1行単位で読み込むFileRead_getsで読み込み、その読み込んだ文字列をatoiなどで整数に変換できるのでしょうか?
例えば上のテキストファイルの1行目を読み込むと文字列として「123」と変数に格納されるわけですが、この123をatoiなどで整数変換できるか?という質問です。
回答よろしくお願いします。

アバター
みけCAT
記事: 6275
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: 11章のcsvデータの読み込みについて

#5

投稿記事 by みけCAT » 6年前

sql さんが書きました:付加質問なのですが、

コード:

123
1
21
のようなテキストファイルを1行単位で読み込むFileRead_getsで読み込み、その読み込んだ文字列をatoiなどで整数に変換できるのでしょうか?
例えば上のテキストファイルの1行目を読み込むと文字列として「123」と変数に格納されるわけですが、この123をatoiなどで整数変換できるか?という質問です。
回答よろしくお願いします。
もちろんできるはずです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

sql

Re: 11章のcsvデータの読み込みについて

#6

投稿記事 by sql » 6年前

みけCAT様、度々回答ありがとうございます。今までの質問の応用となりますが、自分のやりたいことをコードにしてみましたので皆様のご意見を伺いたいです。
コード

コード:

//header

typedef struct
{
	char stage_name[256];
	char attri[256];

	int map[100][5];//1000x5(1000というのは読み込むテキストファイルによって何行かわからないので)

}t_stage;//ステージについて
//---------------------------------------------------

//テキストファイルなどの読み込み
class C_Load_File
{
private:
	t_stage *stage;
	bool flag;//trueだったら「読み込み終わった」ということ


	char file_name[128];
	int FP;
	int input[64];
	char inputc[64];
	char temp_str[256];
public:
	C_Load_File(char file_name);
	~C_Load_File();
};





//cpp
C_Load_File::C_Load_File()
{
	flag = false;
	memset(stage,0,sizeof(t_stage));//構造体初期化

	file_name[128] = {"aaa.txt"};;//ファイル

	FP = FileRead_open(file_name);//ファイルのオープン

	if(FP == NULL)
	{
		printfDx("Load_error:(Class Load_File)");
		return;
	}

	int n=-1;
	while(1)
	{
		if(n == -1)
		{
			for(int i = 0 ; i < 2 ; i++)
			{
				FileRead_gets(temp_str , 256 , FP);//最初の2行を読み込む(※1行単位で)
				switch(i)
				{
					case 0: stage->stage_name[256]	= temp_str[256];break;
					case 1: stage->attri[256]		= temp_str[256];break;
				}

				if(FileRead_eof( FP ) != 0)//ファイルの終わりなら
				{
					goto EXFILE;//終了
				}
			}
		}
		else if(n >= 0)
		{
			for(int i = 0 ; i < 5 ; i++)//[][5]なので
			{
				for(int s = 0 ; s < 64 ; s++)
				{
					inputc[s] = input[s] = FileRead_getc(FP);//1文字読みこみ

					if(inputc[s]=='/')//スラッシュがあれば
					{
						while(FileRead_getc(FP) != '\n');//改行までループ
						i=-1;//カウンタを最初に戻して
						continue;
					}
                    if(input[s]==',' || input[s]=='\n')//カンマか改行なら
					{
                        inputc[s]='\0';//そこまでを文字列とし
                        break;
					}
					if(input[s]==EOF)//ファイルの終わりなら
					{
                            goto EXFILE;//終了
                    }
				}
				stage->map[n][i] = atoi(inputc);//格納
			}
		}
		n++;
	}

EXFILE:
	{
		FileRead_close(FP);
		flag = true;
	}
}
aaa.txt

コード:

ステージ名
属性
//
//以下はマップ
0,0,0,0,0
1,0,0,1,0
0,0,0,0,0
1,1,0,0,0
0,0,0,1,0
やりたいことを説明しますと、上記のaaa.txtは文字列と、数値が混在しているのですが(txt上では文字扱いですが・・)、上の2行の文字列部分だけ1行単位で読み込み、マップの数値は整数変換して二次元配列に格納したいです。


ここで疑問に思ったことと質問があります。
上記のコードで、構造体の初期化をmemsetでやっていますが、構造体の要素にはchar型もあります。memsetではおかしなこと(漠然としていますが)になってしまうのでしょうか?ですので、Initたる関数を用意してコンストラクタのmemsetの位置におけばと思ったのですが・・意見を聞かせてください
次の疑問は「FileRead_gets」と「FileRead_getc」です。最初の2行は前者で1行単位で読み込むのですが、マップの箇所は後者の1文字単位で読み込みます。上記のコードですと、「n = -1」のときに前者、「n >= 0」のときに後者で読み込むのですが、1文字単位で読み取る時に、先に2行を読み取っているので「//」から読み取る感じになっているのでしょうか?要は、前者から後者に移り変わる時に読み込む行はそのままになるのでしょうか?それとも

コード:

 for(i=0;i<2;i++)//最初の2行読み飛ばす
                while(FileRead_getc(FP)!='\n');
と書いて、3行目から読み込むようにしなければいけないのでしょうか?
次に「EXFILE」のところは上記コードのようにして処理を2行書けるのでしょうか?
最後に、自分の書いたコードは、「やりたいこと」の通りになるのでしょうか?
実行する環境が手元にないので・・

ご教授お願いします。

アバター
みけCAT
記事: 6275
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: 11章のcsvデータの読み込みについて

#7

投稿記事 by みけCAT » 6年前

sql さんが書きました:構造体の初期化をmemsetでやっていますが、構造体の要素にはchar型もあります。memsetではおかしなこと(漠然としていますが)になってしまうのでしょうか?ですので、Initたる関数を用意してコンストラクタのmemsetの位置におけばと思ったのですが・・意見を聞かせてください
普通の「構造体」なら問題ないはずですが、ここで扱っているのはC++のクラスですね。
私にはわからないので保留にします。(仕様書を持っている人待ち?)

コードを読むと、不定の値のポインタに対しmemsetしていますね。アクセス違反になる可能性が非常に高いです。
sql さんが書きました:上記のコードですと、「n = -1」のときに前者、「n >= 0」のときに後者で読み込むのですが、1文字単位で読み取る時に、先に2行を読み取っているので「//」から読み取る感じになっているのでしょうか?
多分そうだと思います。
sql さんが書きました:「EXFILE」のところは上記コードのようにして処理を2行書けるのでしょうか?
はい。
sql さんが書きました:自分の書いたコードは、「やりたいこと」の通りになるのでしょうか?
なりません。コンパイルエラーです。
・36行目:仮引数の宣言が足りません。
・41行目:型が違います。
また、以下のような問題もあります。
・8行目:コメントとコードが矛盾しています。
・22~25行目:コンストラクタのローカル変数で良さそうな変数がメンバになっています。(仕様が不明なので無駄と断定できない)
・39行目:不定の値の(可能性が高い)ポインタが指すアドレスにアクセスしています。
・41行目:確保した領域の外にアクセスしています。
・45行目:整数とポインタを比較しています。
・61行目:確保した領域の外にアクセスしています。
・62行目:確保した領域の外にアクセスしています。
・82行目:ここでiに-1を代入すると、確保した領域の外に95行目でアクセスする可能性があります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

sql

Re: 11章のcsvデータの読み込みについて

#8

投稿記事 by sql » 6年前

みけCAT様、返信ありがとうございます。
書き間違えなどくだらないミスが多くて申し訳ありません。
訂正

コード

コード:

//header
 
typedef struct
{
    char stage_name[256];
    char attri[256];
 
    int map[1000][5];//1000x5(1000というのは読み込むテキストファイルによって何行かわからないので)
 
}t_stage;//ステージについて
//---------------------------------------------------
 
//テキストファイルなどの読み込み
class C_Load_File
{
private:
    t_stage *stage;
    bool flag;//trueだったら「読み込み終わった」ということ
   
 
    char file_name[128];
public:
    C_Load_File(char temp_file_name);
    ~C_Load_File();
};
 
 
 
 
 
//cpp
C_Load_File::C_Load_File(char temp_file_name)
{
  int FP;
    int input[64];
    char inputc[64];
    char temp_str[256];
  
    flag = false;
    memset(stage,0,sizeof(t_stage));//構造体初期化
 
    file_name[128] = temp_file_name;//外部から(引数)
 
    FP = FileRead_open(file_name);//ファイルのオープン
 
    if(FP == NULL)
    {
        printfDx("Load_error:(Class Load_File)");
        return;
    }
 
    int n=-1;
    while(1)
    {
        if(n == -1)
        {
            for(int i = 0 ; i < 2 ; i++)
            {
                FileRead_gets(temp_str , 256 , FP);//最初の2行を読み込む(※1行単位で)
                switch(i)
                {
                    case 0: stage->stage_name[256]  = temp_str[256];break;
                    case 1: stage->attri[256]       = temp_str[256];break;
                }
 
                if(FileRead_eof( FP ) != 0)//ファイルの終わりなら
                {
                    goto EXFILE;//終了
                }
            }
        }
        else if(n >= 0)
        {
            for(int i = 0 ; i < 5 ; i++)//[][5]なので
            {
                for(int s = 0 ; s < 64 ; s++)
                {
                    inputc[s] = input[s] = FileRead_getc(FP);//1文字読みこみ
 
                    if(inputc[s]=='/')//スラッシュがあれば
                    {
                        while(FileRead_getc(FP) != '\n');//改行までループ
                        s=-1;//カウンタを最初に戻して
                        continue;
                    }
                    if(input[s]==',' || input[s]=='\n')//カンマか改行なら
                    {
                        inputc[s]='\0';//そこまでを文字列とし
                        break;
                    }
                    if(input[s]==EOF)//ファイルの終わりなら
                    {
                            goto EXFILE;//終了
                    }
                }
                stage->map[n][i] = atoi(inputc);//格納
            }
        }
        n++;
    }
 
EXFILE:
    {
        FileRead_close(FP);
        flag = true;
    }
}
一応直しました、ご意見を聞かせてください。また、
みけCAT さんが書きました:・61行目:確保した領域の外にアクセスしています。
・62行目:確保した領域の外にアクセスしています。
の改善方法も教えてください。
質問に関しては前回の自分の返信と同じです。

よろしくお願いします。

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

Re: 11章のcsvデータの読み込みについて

#9

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

とりあえずここだけ。
sql さんが書きました:
みけCAT さんが書きました:・61行目:確保した領域の外にアクセスしています。
・62行目:確保した領域の外にアクセスしています。
の改善方法も教えてください。
確保した領域の外にアクセスしているというのは、
stage->stage_name[256] = temp_str[256];
となってる部分です。
char stage_name[256]と宣言したら、
配列の添え字は0始まりなので、0~255しか指定できないのです。
まあそれよりも文字列として扱ってないよね。
文字を代入しようとしてますね。
文字列は文字の集まりですよ?そこは分かっていますか?

文字列としてコピーしたいならstrcpyを使用してください。
strcpy(stage->stage_name, temp_str);
written by へにっくす

アバター
みけCAT
記事: 6275
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: 11章のcsvデータの読み込みについて

#10

投稿記事 by みけCAT » 6年前

・コンストラクタに渡す仮引数はconst char*型の方がいいのではないですか?
・まだmemsetに渡しているポインタが不定のままです。そもそもなぜstage_t型のポインタを持っているのですか?
・EXFILE:の後の処理は、それだけを{}で囲む必要はないはずです。
・59行目で渡しているバッファサイズは、決め打ちせずにsizeof(temp_str)を使用したほうがいい。
・42行目で確保した領域の外にアクセスしています。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

sql

Re: 11章のcsvデータの読み込みについて

#11

投稿記事 by sql » 6年前

返信ありがとうございます。指摘を頂いたところを考え、コードを直しました。

コード:

#include "DxLib.h"
#include "Load_File.h"

#include <string.h>//修正点

C_Load_File::C_Load_File(const char *temp_file_name)//(修正点)
{
    int FP;
    int input[64];
    char inputc[64];
    char temp_str[256];
  
    flag = false;
    memset(&stage,0,sizeof(t_stage));//構造体初期化(修正点)
 
	strcpy(file_name , temp_file_name);//下記からこっちに変更(修正点)
    //file_name[128] = temp_file_name;//外部から(引数)
 
    FP = FileRead_open(file_name);//ファイルのオープン
 
    if(FP == NULL)
    {
        printfDx("Load_error:(Class Load_File)");
        return;
    }
 
    int n=-1;
    while(1)
    {
        if(n == -1)
        {
            for(int i = 0 ; i < 2 ; i++)
            {
                FileRead_gets(temp_str , sizeof(temp_str) , FP);//最初の2行を読み込む(※1行単位で)(修正点)
                switch(i)
                {//文字列のコピー(修正点)
					case 0: strcpy(stage.stage_name , temp_str); //stage->stage_name[256]  = temp_str[256];break;
                    case 1: strcpy(stage.attri , temp_str); //stage->attri[256]       = temp_str[256];break;
                }
 
                if(FileRead_eof( FP ) != 0)//ファイルの終わりなら
                {
                    goto EXFILE;//終了
                }
            }
        }
        else if(n >= 0)
        {
            for(int i = 0 ; i < 5 ; i++)//[][5]なので
            {
                for(int s = 0 ; s < 64 ; s++)
                {
                    inputc[s] = input[s] = FileRead_getc(FP);//1文字読みこみ
 
                    if(inputc[s]=='/')//スラッシュがあれば
                    {
                        while(FileRead_getc(FP) != '\n');//改行までループ
                        s=-1;//カウンタを最初に戻して
                        continue;
                    }
                    if(input[s]==',' || input[s]=='\n')//カンマか改行なら
                    {
                        inputc[s]='\0';//そこまでを文字列とし
                        break;
                    }
                    if(input[s]==EOF)//ファイルの終わりなら
                    {
                            goto EXFILE;//終了
                    }
                }
                stage->map[n][i] = atoi(inputc);//格納
            }
        }
        n++;
    }
 
EXFILE:
    {
        FileRead_close(FP);
        flag = true;
    }
}
自分なりに直してみました。
おかしな点などありますでしょうか?(まだ実行できない状態なので・・・)
よろしくお願いします。

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

Re: 11章のcsvデータの読み込みについて

#12

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

37、38行目
switchの構文をお忘れですか。
break;までコメントしてどうすんねん。

14行目
memsetで&stageと直されてますが、
ヘッダも直してるのかな?(載ってないので・・・)

あとみけCATさんの指摘で直してない箇所がありますよ。
written by へにっくす

アバター
みけCAT
記事: 6275
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: 11章のcsvデータの読み込みについて

#13

投稿記事 by みけCAT » 6年前

・14行目:正しく直っているかこれだけでは判定できません。Load_File.hの内容を貼ってください。
・21行目:あえてNULLを使用するより、DXライブラリの仕様でエラーとして決まっている0と比較した方がいいと思います。
・27~28行目、74行目:forループにして、nに関するバッファオーバーラン防止もつけた方がいいと思います。
・37~38行目:break;がありません(無効化されています)。この場合はたまたま正しい動作っぽくなりそうですが。
・57行目:テキストファイルの最終行が/を含み、かつその行の最後が改行コードで終わっていない場合に無限ループになりそうです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

閉鎖

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