メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#31

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

プログラマ見習い さんが書きました:
6年前
では256種類を超えるにはunsigned char型をunsigned int型にすれば良いのではと思い
いくらmapdataの要素を格納する一時変数をunsigned int型にしても、
肝心のmapdataの要素(やキャラクターのkind)がcharのままでは、
charの範囲を超えるデータを読み込むことはできません。
したがって、拡張するならここも拡張するべきです。
プログラマ見習い さんが書きました:
6年前
一応、新しいのをアップロードします。間違っている部分をもう少し詳しく教えてください。

map0.txt

コード:

 

0000000000111111111100000000001111111111
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000AAA000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000AAA000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000111111111100000000001111111111

 
map1.txt

コード:

 
00,00,00,00,00,00,00,00,00,00,11,11,11,11,11,11,11,11,11,11,00,00,00,00,00,00,00,00,00,00,11,11,11,11,11,11,11,11,11,11
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,AA,AA,AA,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,AA,AA,AA,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
00,00,00,00,00,00,00,00,00,00,11,11,11,11,11,11,11,11,11,11,00,00,00,00,00,00,00,00,00,00,11,11,11,11,11,11,11,11,11,11

 
まだ空行やスペースだけの行が入っていますね。
もちろん、このようなデータに対応したいのであれば、
空行やスペースだけの行を無視するコードを入れればいいでしょう。
もしくは、「十六進数として有効なデータ以外を無視する」でもいいでしょう。
(私の#21のコードは、「あらかじめ決めた数種類のデータ以外を無視する」ことで、対応させています)
プログラマ見習い さんが書きました:
6年前
 みけCATさんが投稿してくれた、strchrとstrcmpのソースコードについての返答が遅れてすみませんでした。
 貴重なアドバイスをありがとうございます。

 コンパイルすると、残念ながら、画面は描画されませんでした。
 ソースコードを入れておきます。
この#28のコードについて、「'11'」を「(char)0x11」に、「'AA'」を「(char)0xAA」に、
それぞれ3箇所書き換えて実行したところ、手元の環境で画像が表示されることを確認しました。
したがって、問題は処理系定義の複数文字の文字定数を使っていることであると考えられます。
プログラマ見習いさんの環境ではどうなりますか?
一応、データファイルも添付します。
添付ファイル
media.zip
#28用データファイル
(1.72 KiB) ダウンロード数: 638 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

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

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#32

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

オフトピック
プログラマ見習い さんが書きました:
6年前
かめのこのこのさん。
プログラマ見習い さんが書きました:
6年前
みけCTAさん。
名前の間違いがみられますね。
ここもコピペするといいかもしれません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#33

投稿記事 by かずま » 6年前

JIS X3010:2003 (ISO/IEC 9899:1999) さんが書きました: 6.4.4.4文字定数
 ...
意味規則 単純文字定数は,型 int をもつ。1 バイトの実行文字に対応する
単一の文字を含む単純文字定数の値は,対応付けた文字の表現を整数として
解釈した数値とする。2 文字以上を含む(例えば'ab')又は 1 バイトの実行
文字で表現できない文字若しくは逆斜線表記を含む単純文字定数の値は,
処理系定義とする。
'ab' が処理系定義なので、調べてみます。

コード:

#include <stdio.h>

int main(void)
{
	printf("%x\n", 'a');
	printf("%x\n", 'b');
	printf("%x\n", 'ab');
}
VC++ と gcc の実行結果は同じ。

コード:

61
62
6162
先頭文字が上位バイト、後続文字が下位バイトだと分かったので
mapdata と .kind を short(16ビット) にします。
これで 2文字格納できます。1000種類でも大丈夫です。
画像ファイルは PNG としました。

コード:

#include <DxLib.h>

#define MAPSIZE 32

#define HEIGHT 15
#define WIDTH 40

#define CHARA (HEIGHT * WIDTH)

struct ImageData {
	int kabe01;
	int teki0A[2];
};

struct ImageData im;

struct CharaData {
	short kind; // ★ short: 256種類以上格納するため
	int anime_pattern;
	int x;    // ★ DrawGraph に使用するので float ではなく int
	int y;
	int body; //体力
};

struct CharaData charadata[CHARA];
int counter; // ★ CHARA個のうち実際には counter個を使用

unsigned int g_anime_counter = 0;

short mapdata[HEIGHT][WIDTH]; // ★ short: 256種類以上格納するため

void MapLoad()
{
	char buf[1024];
	sprintf_s(buf, 1024, "media/map%d.txt", 1);
	int fp = FileRead_open(buf);  // ★ fp はファイルハンドル
	if (fp == 0) { printfDx("ファイル読み込みエラー"); return; }
/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		char *p = buf;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = p[0]<<8 | p[1]; // ★ 先頭文字が short の上位バイト
			p += 3;
		}
	}
/////////////////////////////ここまで////////////////////////////
	FileRead_close(fp);
}

void Load()
{
	im.kabe01 = LoadGraph("media/kabe01.png");
	LoadDivGraph("media/teki0A.png", 2, 2, 1, 32, 32, im.teki0A);
}

void Init()
{
	ZeroMemory(charadata, sizeof(charadata));  // ★ ここで初期化が適切
	counter = 0;
	for (int y = 0; y < HEIGHT; y++) {
		for (int x = 0; x < WIDTH; x++) {
			short c = mapdata[y][x];	// ★ short
			charadata[counter].kind = c;
			charadata[counter].x = x * MAPSIZE;
			charadata[counter].y = y * MAPSIZE;
			switch (c) {
			case '11':
				charadata[counter].body = 1;
				break;
			case 'AA':
				charadata[counter].anime_pattern = 2;
				charadata[counter].body = 1;
				break;
			}
			if (charadata[counter].body) {  // ★ charaValid の代わり
				// mapdata[y][x] = '0';  // ★ 不要では? '0' と '00' は異なる値
				counter++;
			}
		}
	}
}

void Move()
{
	for (int i = 0; i < counter; i++) {  // ★ counter個を描画
		short c = charadata[i].kind; // ★ short
		switch (c) {
		case '11':
			DrawGraph(charadata[i].x, charadata[i].y, im.kabe01, TRUE);
			break;
		case 'AA':
			int a = g_anime_counter / 20 % charadata[i].anime_pattern;
			DrawGraph(charadata[i].x, charadata[i].y, im.teki0A[a], TRUE);
			break;
		}
		charadata[i].x = charadata[i].x - 1;
	}
}

int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC)
{
	if (ChangeWindowMode(TRUE) != 0) return -1;
	if (DxLib_Init() != 0) return -1;
	if (SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;

	MapLoad();
	Load();
	Init();

	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
		if (ClearDrawScreen() != 0) return -1;
		Move();
		g_anime_counter++;
		if (ScreenFlip() != 0) return -1;
	}

	if (DxLib_End() != 0) return -1;
	return 0;
}

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#34

投稿記事 by かずま » 6年前

コード:

#include <stdio.h>

int main(void)
{
	printf("%x\n", 'ア');
	printf("%x\n", 'イ');
	printf("%x\n", 'アイ');
}
Visual C++ での実行結果

コード:

ffffffb1
ffffffb2
b1b2
char *p = buf; で
mapdata[y][x] = p[0]<<8 | p[1]; だと
'アア' が 0xffffffb1 になるので、次のように変更します。

コード:

		unsigned char *p = (unsigned char *)buf; // ★
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = p[0]<<8 | p[1]; // ★ 先頭文字が上位
			p += 3;
		}
map1.txt の中の XX,XX,XX,.. の XX は 16進数でなくなったので
文字なら何でも構いません。
'0'~'9'、'A'~'Z'、'a'~'z' の 62文字に限らず
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ の 32文字も使えます。
スペースも使えます。
。「」、・ヲァィゥェォャュョッーアイウエオ...ラリルレロワン゙゚ の 63文字も使えます。
全部で 158文字なので、158x158 = 24964種類の mapdata を扱えます。

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#35

投稿記事 by かずま » 6年前

かずま さんが書きました:
6年前
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ の 32文字も使えます。
スペースも使えます。
。「」、・ヲァィゥェォャュョッーアイウエオ...ラリルレロワン゙゚ の 63文字も使えます。
全部で 158文字なので、158x158 = 24964種類の mapdata を扱えます。
mapdata や .kind を short から unsigned short に変更が必要です。
unsigned short c = もです。

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#36

投稿記事 by プログラマ見習い » 6年前

皆さん。貴重なアドバイスありがとうございます。
事情があって返信が遅れています。
今回はメモ帳の問題に絞って返信します。

空行やスペースの問題についてですが、投稿の際に、私がコード貼り付けタグを使ったのが原因ではないかと思います。
今度はmap0.txtとmap1.txtを添付ファイルとして送ります。
問題点がないかどうかのお確かめをお願いします。

みけCATさん。
かめのこのこのこ さん。
名前を間違ってすみません。
名前もコピペします。
添付ファイル
map1.txt
(1.77 KiB) ダウンロード数: 628 回
map0.txt
(632 バイト) ダウンロード数: 636 回

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#37

投稿記事 by プログラマ見習い » 6年前

 かずまさん。
 かすまさんが投稿してくれたソースコードをコンパイルしたら、画像が描画されました。
 その後、いろいろと調べていくと、まず自分のint型やchar型などの変数に対する認識がいかに甘かったが分かりました。
 変数とビット数やバイト数との関係が、このような場合に生きてくるのかと言う驚きです。。
 文字が内部では8ケタの整数で処理される事の意味を今まで理解していませんでした。

 char型が8ビットで、short型が16ビット、int型は32と64の2種類を確認しました。

 以下のソースコードについても、自分で調べてみました。
/////////////////////////////ここから////////////////////////////
for (int y = 0; y < HEIGHT; y++) {
if (FileRead_gets(buf, 1024, fp) == -1) break;
char *p = buf;
for (int x = 0; x < WIDTH; x++) {
mapdata[y][x] = p[0]<<8 | p[1]; // ★ 先頭文字が short の上位バイト //☆<<はシフト演算子。
//☆シフト演算子は、<<で右シフト。>>で左シフト。
//☆<<8で、8ビットシフト。
//「|」はビットごとのOR。
p += 3; //☆は、「00,」「11,」「AA,」で、三文字分あるので、「3」だけ増やす。
}
}
/////////////////////////////ここまで////////////////////////////

 どの程度理解で出来たかと言うと、誤解を恐れずに言うと、内部で処理されている様子を頭で想像出来る程度と言う事です。具体的な事については確信を持てません。
 特に今でも理解出来ていないのが、「mapdata[y][x] = p[0]<<8 | p[1];」の部分のp[0]とp[1]の意義についてです。
 今、皆さんのアドバイスを元に、00,11,AA,など、カンマで区切られた整数ないし文字をマップデータとして読み込みための自分が使いこなせる解決策を考える所です。

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#38

投稿記事 by かずま » 6年前

ポインタが分からないのなら、次のようにも書けます。

コード:

/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		int i = 0;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = buf[i]<<8 | buf[i+1];
			i += 3;
		}
	}
/////////////////////////////ここまで////////////////////////////
ビット演算が苦手なら、次のようにも書けます。

コード:

/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		int i = 0;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = buf[i]*256 + buf[i+1];
			i += 3;
		}
	}
/////////////////////////////ここまで////////////////////////////

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#39

投稿記事 by プログラマ見習い » 6年前

かずまさん。

貴重なアドバイス、ありがとうございます。
上記の二つのソースコードに対する返信は、研究に時間がかかるので、後ほどします。

 皆さんのアドバイスを参考にして、自分が使いこなせそうな解決策を一つ選びました。
 内容を大まかに述べると、strtol関数を利用する事と、10進数をベースにする事です。
 実質、00,01,11のような二桁のカンマで区切られた整数を利用する事にとどめて、AA,BBなどのアルファベットを利用しない方法です。

 ソースコードを載せます。添付ファイルmap2.txtを載せます。中身は00,01,11の三種類です。

コード:

#include <DxLib.h>

#define MAPSIZE 32

#define HEIGHT 15
#define WIDTH  40

#define CHARA (HEIGHT * WIDTH)

struct ImageData {
	int kabe01;
	int teki0A[2];
};

struct ImageData im;

struct CharaData {
	//char kind; //☆
        int kind; //☆
        //short kind //☆
	int anime_pattern;
	int x, y;
	int body;
};
int counter;

struct CharaData charadata[CHARA];
unsigned int g_anime_counter = 0;

//char mapdata[HEIGHT][WIDTH];
int mapdata[HEIGHT][WIDTH];

void MapLoad()
{
	ZeroMemory(charadata, sizeof(charadata));

	char buf[1024];
	  //strcpy(buf, "media/map%d.txt",2); //☆マップファイルをmap2.txtに変更。
        sprintf_s(buf, "media/map%d.txt",2); //☆s_printf_s関数に変更。
         //sprintf_s(buf, "media/map%d.txt",3); //☆map3.txtはAの文字が入っているので、読み込もうとすると「ファイル読み込みエラー」になる。
	int fh = FileRead_open(buf);
	if (fh == NULL) { printfDx("ファイル読み込みエラー"); return; }


//////////ここから//////////

	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fh) == -1) break;
		char *p = buf;                          // ★
		for (int x = 0; x < WIDTH; x++) {       // ★
			   //mapdata[y][x] = strtol(p, &p, 16);  // ★
                        mapdata[y][x] = strtol(p, &p, 10); //☆10進数に変更
			p++; // skip ','                    // ★ ☆スキップ
		}                                       // ★
	}

//////////ここまで//////////

	FileRead_close(fh);
}

void Load()
{
	im.kabe01 = LoadGraph("media/kabe01.png"); //☆今回は画像形式をpngにする。
	LoadDivGraph("media/teki0A.png", 2, 2, 1, 32, 32, im.teki0A);
}

void Init()
{
	counter = 0;
	for (int y = 0; y < HEIGHT; y++) {
		for (int x = 0; x < WIDTH; x++) {
			 //unsigned char c = mapdata[y][x];  // ★
                        //int c=mapdata[y][x]; //☆unsignedなしでも描画された。
                        unsigned int c=mapdata[y][x]; //☆
                         //short c=mapdata[y][x]; //☆
			charadata[counter].kind = c;
			charadata[counter].x = x * MAPSIZE;
			charadata[counter].y = y * MAPSIZE;
			switch (c) {
			  //case 0x11:  // ★
                         case 01: //☆10進数に変更のため変更。
				charadata[counter].body = 1;
				mapdata[y][x] = '0';
				counter++;
				break;
			   //case 0xAA:  // ★
                          case 11: //☆10進数変更のため変更
				charadata[counter].anime_pattern = 2;
				charadata[counter].body = 1;
				 //mapdata[y][x] = '00'; //☆間違えて'0'のままにしていた。
                                mapdata[y][x]=00; //☆00に変更
				counter++;
				break;
			}
		}
	}
}

void Move()
{
	for (int i = 0; i < counter; i++) {
		 //unsigned char c = charadata[i].kind;  // ★
                //int c=charadata[i].kind;  //☆unsignedなしでも描画された。
                unsigned int c=charadata[i].kind;  //☆
                //short int c=charadata[i].kind;  //☆
		switch (c) {
		 //case 0x11:  // ★
                 case 01: //☆10進数に変更のため変更。
			DrawGraph(charadata[i].x, charadata[i].y, im.kabe01, TRUE);
			break;

		 //case 0xAA:  // ★
                 case 11: //☆10進数に変更のため変更。
			int a = g_anime_counter / 20 % charadata[i].anime_pattern;
			DrawGraph(charadata[i].x, charadata[i].y, im.teki0A[a], TRUE);
			break;
		}
		charadata[i].x = charadata[i].x - 1;
	}
}

int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC)
{
	if (ChangeWindowMode(TRUE) != 0) return -1;
	if (DxLib_Init() != 0) return -1;
	if (SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;

	MapLoad();
	Load();
	Init();

	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
		if (ClearDrawScreen() != 0) return -1;
		Move();
		g_anime_counter++;
		if (ScreenFlip() != 0) return -1;
	}

	if (DxLib_End() != 0) return -1;
	return 0;
}


 解決策の一つに、以上の方法を選んだのは、strtol関数に魅力を感じた事と、整数以外の文字を利用する事に限界を感じたからです。
 外部から読み込んだデータを整数に変換する方法(整数ベース)と比べて、文字や文字列のまま扱う方法(文字列ベース)は、複雑に感じて苦手意識から抜け出せません。

 私はファイル読み込みに対する苦手意識と、いろいろ試していけば何とかなるだろうと言う矛盾した意識をもって挑戦してきたのですが、自力では出来なかったのは周知のとおりです。
 
 それでもstrtol関数を利用した整数ベースについて自分なりとは言え理解出来る所まで辿り着けましたが、文字列ベースについては、まだです。

 文字列ベースでの解決策は、もう少し研究してみる事にします。

 ただ、strtol関数を利用した場合について、理解したとはいっても自分なりのイメージ過ぎないで、問題点があると思います。自分なりに理解、と言うよりも、イメージした部分を以下に示します。問題点があれば、アドバイスをお願いします。

コード:

//////////ここから//////////

	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fh) == -1) break;
		char *p = buf;                          // ★
		for (int x = 0; x < WIDTH; x++) {       // ★
			   //mapdata[y][x] = strtol(p, &p, 16);  // ★
                        mapdata[y][x] = strtol(p, &p, 10); //☆10進数に変更
			p++; // skip ','                    // ★ ☆スキップ
		}                                       // ★
	}

//////////ここまで//////////


strtol関数=文字列をlong型の整数に変換する関数。
#include <stdlib.h>
long strtol(const char *s, char **endptr, int base);
const char *s 第1引数 変換する対象の文字列を指定
char **endptr 第2引数 変換不可能な文字列を指定?
int base 基数 10進数や16進数などに変換

 ポインタは配列ではありませんが、あえて格納数の決まっていない配列と考える。そう考えないとイメージ出来ないからです。許して下さい。

char *p=buf;は、1024あるデータをいくつかのp[]に分割するための設定をするが、いくつかまではこの時点では指定されていない。
 for (int x = 0; x < WIDTH; x++)によって、WIDTH個まで分割される。

 bufは、base進数ずつ分割される。ここでは10進数ごとに分割される。
 なお、WIDTH個まで分割される前に、bufが不足すると、容量オーバーが起こるので、あらかじめ多めに設定しておく。bufが余るくらいが丁度よい。

 mapdata[y][x]=strtol(p, &p, 10);では、pと10を置く事で、いくつかあるp[]を一つずつ10進数に変換して、&pを置く事で、p[]が文字列から整数に変換される時に、外見まで変換されないようにする。
 例えば、p[]の中身が11の場合、メモ帳に書かれた11は、読み込まれた時点では文字列であるが、strtol関数によって10進数の整数に変換される場合、11がコンピューター内では11でない数値に変換される事がある。そこで、&pを置く事によって、文字列の11が整数に変換された場合でも、11と言う値が11以外の数値に変換されないようにする。
 p++によって、メモ帳のカンマ「,」を扱わないようにする。例えば、11の場合、p[]の中身は厳密には当初「11,」であるが、strtol(p, &p, 10);によって11の部分が整数に変換された後固定される。その後、p++によって固定されなかった「,」は無視されて、「11,」は「11」となり、「,」FileRead_close(fh);の際に破棄される。
添付ファイル
map2.txt
(1.77 KiB) ダウンロード数: 617 回

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#40

投稿記事 by usao » 6年前

オフトピック
そんな意味わからん長文書く前に,まずはstrtolをググればよくね?
(引数の意味について書こうとして「?」が付いている時点でダメだろ常考…)

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#41

投稿記事 by かずま » 6年前

プログラマ見習い さんが書きました:
6年前
strtol関数=文字列をlong型の整数に変換する関数。
#include <stdlib.h>
long strtol(const char *s, char **endptr, int base);
const char *s 第1引数 変換する対象の文字列を指定
char **endptr 第2引数 変換不可能な文字列を指定?
int base 基数 10進数や16進数などに変換
10進数や 16進数などに変換するのではなく、
10進数や 16進数などの文字列を long型の整数に変換します。

次のコードの実行結果をよく考えてください。

コード:

#include <stdio.h>
#include <stdlib.h>  // strtol

int main(void)
{
	int x, d, o;
	char s1[] = "127", s2[] = "15,72", s3[] = "  8cm";
	char *p1, *p2, *p3;

	x = strtol(s1, &p1, 16);
	d = strtol(s1, &p2, 10);
	o = strtol(s1, &p3, 8);
	printf("\ns1=%p, s1=\"%s\"\n", s1, s1);
	printf("p1=%p, *p1=%02x, x=%d\n", p1, *p1, x);
	printf("p2=%p, *p2=%02x, d=%d\n", p2, *p2, d);
	printf("p3=%p, *p3=%02x, o=%d\n", p3, *p3, o);

	x = strtol(s2, &p1, 16);
	d = strtol(s2, &p2, 10);
	o = strtol(s2, &p3, 8);
	printf("\ns2=%p, s2=\"%s\"\n", s2, s2);
	printf("p1=%p, *p1='%c', x=%d\n", p1, *p1, x);
	printf("p2=%p, *p2='%c', d=%d\n", p2, *p2, d);
	printf("p3=%p, *p3='%c', o=%d\n", p3, *p3, o);

	x = strtol(s3, &p1, 16);
	d = strtol(s3, &p2, 10);
	o = strtol(s3, &p3, 8);
	printf("\ns3=%p, s3=\"%s\"\n", s3, s3);
	printf("p1=%p, *p1='%c', x=%d\n", p1, *p1, x);
	printf("p2=%p, *p2='%c', d=%d\n", p2, *p2, d);
	printf("p3=%p, *p3='%c', o=%d\n", p3, *p3, o);
}
実行結果

コード:


s1=0135FB08, s1="127"
p1=0135FB0B, *p1=00, x=295
p2=0135FB0B, *p2=00, d=127
p3=0135FB0B, *p3=00, o=87

s2=0135FB20, s2="15,72"
p1=0135FB22, *p1=',', x=21
p2=0135FB22, *p2=',', d=15
p3=0135FB22, *p3=',', o=13

s3=0135FB18, s3="  8cm"
p1=0135FB1C, *p1='m', x=140
p2=0135FB1B, *p2='c', d=8
p3=0135FB18, *p3=' ', o=0
プログラマ見習い さんが書きました:
6年前
 ポインタは配列ではありませんが、あえて格納数の決まっていない配列と考える。そう考えないとイメージ出来ないからです。許して下さい。

char *p=buf;は、1024あるデータをいくつかのp[]に分割するための設定をするが、いくつかまではこの時点では指定されていない。
ポインタは配列ではありません。

char c = 'A'; char *p; p = &c; で、
ポインタ p の値は c のアドレスとなり、p は c を指します。
*p は c です。

次は、ポインタが配列の先頭要素に設定された場合について説明します。

char buf = "ABC"; char *p; p = buf; で、
ポインタ p の値は buf[0] のアドレスとなり、p は buf[0] を指します。
*p は buf[0] です。*p の値は 'A' です。
*p は p[0] と書いてもよいのです。

p+1 は、buf[1] のアドレスです。p+1 は buf[1] を指します。
*(p+1) は buf[1] です。*(p+1) の値は 'B' です。
*(p+1) は p[1] と書いてもよいのです。

p は、配列 buf を分割なんかしません。
配列の要素を指すだけです。
*(p+i) または p[ i] の値は、buf[ i] です。
プログラマ見習い さんが書きました:
6年前
 for (int x = 0; x < WIDTH; x++)によって、WIDTH個まで分割される。
mapdata[y] を mapdata[y][x] で参照することにより
個々の要素を参照することを分割といっているのでしょうか?
プログラマ見習い さんが書きました:
6年前
 bufは、base進数ずつ分割される。ここでは10進数ごとに分割される。
 なお、WIDTH個まで分割される前に、bufが不足すると、容量オーバーが起こるので、あらかじめ多めに設定しておく。bufが余るくらいが丁度よい。
buf を分割しているのではありません。
buf の先頭から順に見に行っているだけです。
buf には、WIDTH個の整数文字列があることを想定しています。
もし、 WIDTH個より少なくて、見ているところが buf 内の文字列の最後の '\0'
に達すると strtol は変換に失敗し、0 を返します。
ただそれだけのことです。容量オーバーの意味が分かりません。
プログラマ見習い さんが書きました:
6年前
 mapdata[y][x]=strtol(p, &p, 10);では、pと10を置く事で、いくつかあるp[]を一つずつ10進数に変換して、&pを置く事で、p[]が文字列から整数に変換される時に、外見まで変換されないようにする。
いくつかある p[] って何ですか?
p は buf の中のある位置を指していて、そこの文字から順番に読み取って
数値に変換していき、変換できなくなった文字の位置を p に返します。
引数で値を返すために &p で p のアドレスを渡しています。
「外見まで変換されない」の意味が分かりません。
プログラマ見習い さんが書きました:
6年前
 例えば、p[]の中身が11の場合、メモ帳に書かれた11は、読み込まれた時点では文字列であるが、strtol関数によって10進数の整数に変換される場合、11がコンピューター内では11でない数値に変換される事がある。そこで、&pを置く事によって、文字列の11が整数に変換された場合でも、11と言う値が11以外の数値に変換されないようにする。
 p++によって、メモ帳のカンマ「,」を扱わないようにする。例えば、11の場合、p[]の中身は厳密には当初「11,」であるが、strtol(p, &p, 10);によって11の部分が整数に変換された後固定される。その後、p++によって固定されなかった「,」は無視されて、「11,」は「11」となり、「,」FileRead_close(fh);の際に破棄される。
わけがわかりません。

buf[] = "00,11,34"; だったとします。
buf[0] は '0'、buf[2] は ','、buf[7] は '4'、buf[8] は '\0' です。
char *p = buf; で
p[0] は '0'、p[2] は ','、p[7] は '4'、p[8] は '\0' となります。

val = strtol(p, &p, 10); で
strtol は、p の指すところから順番に文字を見ていきます。
p[0] が '0'、p[1] が '0'、p[2] が ','。
ここで数字でない文字にぶち当たったので、それ以前を整数値 0 に変換し、
返却値として返すので、val は 0 になります。

そして、第2引数の p には、p[2] のアドレスすなわち p + 2 が返されます。
p は 2つ進みました。
それは、buf[2] のアドレスです。
p は buf[2] の ',' を指すようになりました。

p++; で p を一つ進めると、p は buf[3] の '1' を指すようになります。

val = strtol(p, &p, 10); で
strtol は、p の指すところから順番に文字を見ていきます。
p[0] が '1'、p[1] が '1'、p[2] が ','。
ここで数字でない文字にぶち当たったので、それ以前を整数値 11 に変換し、
返却値として返すので、val は 11 になります。

そして、第2引数の p には、p[2] のアドレスすなわち p + 2 が返されます。
p はさらに 2つ進みました。
それは、buf[5] のアドレスです。
p は buf[5] の ',' を指すようになりました。

p++; で p を一つ進めると、p は buf[6] の '3' を指すようになります。
val = strtol(p, &p, 10); で
strtol は、p の指すところから順番に文字を見ていきます。
p[0] が '3'、p[1] が '4'、p[2] が '\0'。
ここで数字でない文字にぶち当たったので、それ以前を整数値 34 に変換し、
返却値として返すので、val は 34 になります。

そして、第2引数の p には、p[2] のアドレスすなわち p + 2 が返されます。
p はさらに 2つ進みました。
それは、buf[8] のアドレスです。
p は buf[8] の '\0' を指すようになりました。

p は buf を分割しているのではなく、buf の中の文字を順番に見ていくことに
使用されています。

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#42

投稿記事 by かずま » 6年前

かずま さんが書きました:
6年前
char buf = "ABC"; char *p; p = buf; で、
すみません。次のように訂正します。
char buf[4] = "ABC"; char *p; p = buf; で、

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#43

投稿記事 by プログラマ見習い » 6年前

ググって調べて頭の中で整理したものを文章に書いただけです。本当にすみません。
ただ、本当に理解出来なかったので、間違って理解してる部分を正すために、あえて投稿しただけです。
不評だったので、このような文章を投稿するのはやめて、今後は素直にこの部分が理解出来なかったとだけ投稿する事にします。
ただ、自分も他人任せではなく、ちゃんと考えていることを示したかっただけです。

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#44

投稿記事 by かずま » 6年前

プログラマ見習い さんが書きました:
6年前
ググって調べて頭の中で整理したものを文章に書いただけです。本当にすみません。
ただ、本当に理解出来なかったので、間違って理解してる部分を正すために、あえて投稿しただけです。
不評だったので、このような文章を投稿するのはやめて、今後は素直にこの部分が理解出来なかったとだけ投稿する事にします。
ただ、自分も他人任せではなく、ちゃんと考えていることを示したかっただけです。
謝る必要はありません。
考え方を書いてもらったほうが、何を間違えているのかわかるので、
それに合わせて回答できます。

こちらの回答が理解できたのかどうか、
こちらの回答に疑問点がないかどうか、
を報告してもらえれば幸いです。

用語の使い方が適切でないように思います。例えば、タイトルの
「メモ帳からカンマ付き文字列のデータの読み込みが出来ません。」

メモ帳は、テキストファイルを作成編集するテキストエディタという
アプリケーションプログラムです。
カンマ付き文字列というのは、テキストファイル中のデータです。
メモ帳からデータを読み込むのではなく、
メモ帳で作ったテキストファイルからデータを読み込むのです。
テキストファイルは、メモ帳以外のツールで作ることができます。
メモ帳は関係ありません。

ということで、
「例えば、p[]の中身が11の場合、メモ帳に書かれた11は、」とか
「p++によって、メモ帳のカンマ「,」を扱わないようにする。」という
表現は変です。
buf の中の "11" や ',' を p が指すようになるということでしょう。
strtol関数によって10進数の整数に変換される場合、11がコンピューター内では11でない数値に変換される事がある。
10進数の整数に変換されるのではありません。
第1引数の文字列を 10進数だとみなして、それを long型の整数に変換するのです。
"11" という文字列から 11 という整数値を作り出すのです。
11 でない数値に変換される事はありません。
そこで、&pを置く事によって、文字列の11が整数に変換された場合でも、11と言う値が11以外の数値に変換されないようにする。
&p すなわち p のアドレスを第2引数で strtol に渡すことによって、
文字列 "11" を整数値 11 に変換した後の "11" の次の位置を p に
返してもらうのです。
p は buf の中の ',' を指すことになります。
その後、p++によって固定されなかった「,」は無視されて、「11,」は「11」となり、「,」FileRead_close(fh);の際に破棄される。
固定という表現が分かりませんが、p++; により、p は
buf の中で ',' の次の文字を指すようになります。
そして、strtol でそこにある数字列をまた数値に変換できるようになるのです。
FileReadclose ではファイルハンドルの先のデータが破棄されるのであって、
buf や その中の ',' が破棄されることはありません。

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#45

投稿記事 by プログラマ見習い » 6年前

かずまさん。
ありがとうございます。
説明を読ませてもらいました。ああなるほど、そういう事だったのかと言う思いです。strtol関数について調べてみたのですが、かずまさんのように分かりやすく解説してくれている資料を見つける事が出来なかったので、嬉しいです。

strtol関数など、関数やソースコードにいくつか疑問点があったのですが、やはり深入りしてはいけない部分、深入りしない方がいい部分もあるのでしょうか?

次に、以前、///ここから/// ////ここまで///の部分でかずまさんが投稿してくれたソースコードで、一つ疑問点を質問します。コンパイルは2パターンとも問題なく成功しました。

以下のソースコードについてです。
ビット演算が苦手な場合

コード:

/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		int i = 0;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = buf[i]*256 + buf[i+1];
			i += 3;
		}
	}
/////////////////////////////ここまで////////////////////////////
mapdata[y][x] = buf*256 + buf[i+1];の*256の役割がどうしても分かりませんでした。ご教示、お願いします。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#46

投稿記事 by usao » 6年前

左に1bitシフトしたら値は2倍になる(という点については大丈夫か?)
左に8bitシフトしたら値は(2の8乗=256)倍になる

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#47

投稿記事 by プログラマ見習い » 6年前

usaoさん。

「左に1bitシフトしたら値は2倍になる(という点については大丈夫か?)
左に8bitシフトしたら値は(2の8乗=256)倍になる」。

 貴重なアドバイスをありがとうございます。感謝しています。

 2進数でデータが管理されている。ピンときましたと答えます。

 私は以前、かずまさんが投稿してくれた以下のソースコードについて、理解を深めるために、整数ではなく文字の場合で、2文字以外、つまり、1文字の場合、3文字の場合に変更するべきソースコードを調べていました。

コード:

/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		char *p = buf;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = p[0]<<8 | p[1]; // ★ 先頭文字が short の上位バイト
 //☆for文でmapdata[y][x]=p[0]<<8 | p[1];の作業を繰り返しているので、ちゃんと2文字分を代入出来ている。
			p += 3; //☆','をスキップ
		}
	}
/////////////////////////////ここまで////////////////////////////
ポインタが苦手な場合

コード:

/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		int i = 0;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = buf[i]<<8 | buf[i+1];
			i += 3; //☆','をスキップ
		}
	}
/////////////////////////////ここまで////////////////////////////
ビット演算が苦手な場合

コード:

/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		int i = 0;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = buf[i]*256 + buf[i+1];
			i += 3; //☆','をスキップ
                        //3文字の場合はi += 4;
		}
	}
/////////////////////////////ここまで////////////////////////////
 「ビット演算が苦手な場合」については、256の役割が分からなかったのですが、usaoさんのアドバイスのおかげで、こちらも含めて、3つの全てのパターンで、データが1文字の場合と3文字の場合で、読み込んで描画する事に成功しました。
 以下にソースコードを投稿します。添付ファイルは、map3.txt map4.txtです。
 画像は、以前に投降したjpg形式の添付ファイル#2にあります。

文字の場合(1文字の場合) map3.txt
 ///ここから/// ///ここまで///は「ポインタが苦手な場合」を使用。

コード:

#include <DxLib.h>

#define MAPSIZE 32

#define HEIGHT 15
#define WIDTH 40

#define CHARA (HEIGHT * WIDTH)

struct ImageData {
	int kabe01;
	int teki0A[2];
};

struct ImageData im;

struct CharaData {
	//short kind; // ★ short: 256種類以上格納するため
        char kind; //☆1文字の場合はcharに変更。
	int anime_pattern;
	int x,y;
	int body; //体力
};

struct CharaData charadata[CHARA];
int counter; // ★ CHARA個のうち実際には counter個を使用

unsigned int g_anime_counter = 0;

//short mapdata[HEIGHT][WIDTH]; // ★ short: 256種類以上格納するため
char mapdata[HEIGHT][WIDTH]; // ☆

void MapLoad()
{
	char buf[1024];
	sprintf_s(buf, 1024, "media/map%d.txt", 3);
	int fp = FileRead_open(buf);  // ★ fp はファイルハンドル
	if (fp == 0) { printfDx("ファイル読み込みエラー"); return; }
/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		int i = 0;
		for (int x = 0; x < WIDTH; x++) {
			//mapdata[y][x] = buf[i]<<8 | buf[i+1];
                        mapdata[y][x] = buf[i]; //1文字の場合は、<<8 | buf[i+1];をなしにする。*256+buf[i+1]もなし。
			//i += 3; //☆','をスキップ
                        i+=2; //☆1文字の場合は3を2に修正
		}
	}
/////////////////////////////ここまで////////////////////////////
	FileRead_close(fp);
}

void Load()
{
	im.kabe01 = LoadGraph("media/kabe01.jpg");
	LoadDivGraph("media/teki0A.jpg", 2, 2, 1, 32, 32, im.teki0A);
}

void Init()
{
	ZeroMemory(charadata, sizeof(charadata));  // ★ ここで初期化が適切
	counter = 0;
	for (int y = 0; y < HEIGHT; y++) {
		for (int x = 0; x < WIDTH; x++) {
			//short c = mapdata[y][x];	// ★ short
                        char c = mapdata[y][x]; //☆1文字の場合はchar
			charadata[counter].kind = c;
			charadata[counter].x = x * MAPSIZE;
			charadata[counter].y = y * MAPSIZE;
			switch (c) {
			case '1': //☆
				charadata[counter].body = 1;
                                mapdata[y][x] ='0'; //☆
				counter++;
				break;
			case 'A': //☆
				charadata[counter].anime_pattern = 2;
				charadata[counter].body = 1;
                                mapdata[y][x] ='0'; //☆
				counter++;
				break;
			}

		}
	}
}

void Move()
{
	for (int i = 0; i < counter; i++) {  // ★ counter個を描画
		//short c = charadata[i].kind; // ★ short
                char c= charadata[i].kind; //☆1文字の場合はcharに変更
		switch (c) {
		case '1': //☆
			DrawGraph(charadata[i].x, charadata[i].y, im.kabe01, TRUE);
			break;
		case 'A': //☆
			int a = g_anime_counter / 20 % charadata[i].anime_pattern;
			DrawGraph(charadata[i].x, charadata[i].y, im.teki0A[a], TRUE);
			break;
		}
		charadata[i].x = charadata[i].x - 1;
	}
}

int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC)
{
	if (ChangeWindowMode(TRUE) != 0) return -1;
	if (DxLib_Init() != 0) return -1;
	if (SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;

	MapLoad();
	Load();
	Init();

	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
		if (ClearDrawScreen() != 0) return -1;
		Move();
		g_anime_counter++;
		if (ScreenFlip() != 0) return -1;
	}

	if (DxLib_End() != 0) return -1;
	return 0;
}


文字の場合(3文字の場合) map4.txt
 ///ここから/// ///ここまで///は「ポインタが苦手な場合」を使用。//の操作で「シフト演算が苦手な場合」に変更可。

コード:

#include <DxLib.h>

#define MAPSIZE 32

#define HEIGHT 15
#define WIDTH 40

#define CHARA (HEIGHT * WIDTH)

struct ImageData {
	int kabe01;
	int teki0A[2];
};

struct ImageData im;

struct CharaData {
	//short kind; // ★ short: 256種類以上格納するため
        int kind; //☆3文字の場合はint型に変更。
	int anime_pattern;
	int x,y;
	int body; //体力
};

struct CharaData charadata[CHARA];
int counter; // ★ CHARA個のうち実際には counter個を使用

unsigned int g_anime_counter = 0;

//short mapdata[HEIGHT][WIDTH]; // ★ short: 256種類以上格納するため
int mapdata[HEIGHT][WIDTH]; // ☆3文字の場合はint型に変更

void MapLoad()
{
	char buf[1024];
	sprintf_s(buf, 1024, "media/map%d.txt", 4);
	int fp = FileRead_open(buf);  // ★ fp はファイルハンドル
	if (fp == 0) { printfDx("ファイル読み込みエラー"); return; }
/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		int i = 0;
		for (int x = 0; x < WIDTH; x++) {
			//mapdata[y][x] = buf[i]<<8 | buf[i+1];
                        mapdata[y][x] = buf[i]<<16 | buf[i+1]<<8 | buf[i+2]; //3文字の場合は1ケタ目を16ビットシフト。2ケタ目を8ビットシフト。
                        //mapdata[y][x] =buf[i]*256*256+ buf[i]*256+buf[i+1]; //☆ビット演算子を使わない場合はこちら。
			//i += 3; //☆','をスキップ
                        i+=4; //☆1文字の場合は3を4に修正
		}
	}
/////////////////////////////ここまで////////////////////////////
	FileRead_close(fp);
}

void Load()
{
	im.kabe01 = LoadGraph("media/kabe01.jpg");
	LoadDivGraph("media/teki0A.jpg", 2, 2, 1, 32, 32, im.teki0A);
}

void Init()
{
	ZeroMemory(charadata, sizeof(charadata));  // ★ ここで初期化が適切
	counter = 0;
	for (int y = 0; y < HEIGHT; y++) {
		for (int x = 0; x < WIDTH; x++) {
			//short c = mapdata[y][x];	// ★ short
                        int c = mapdata[y][x]; //☆3文字の場合はint型に変更
			charadata[counter].kind = c;
			charadata[counter].x = x * MAPSIZE;
			charadata[counter].y = y * MAPSIZE;
			switch (c) {
			case '111': //☆
				charadata[counter].body = 1;
                                mapdata[y][x] ='000'; //☆
				counter++;
				break;
			case 'AAA': //☆
				charadata[counter].anime_pattern = 2;
				charadata[counter].body = 1;
                                mapdata[y][x] ='000'; //☆
				counter++;
				break;
			}

		}
	}
}

void Move()
{
	for (int i = 0; i < counter; i++) {  // ★ counter個を描画
		//short c = charadata[i].kind; // ★ short
                int c= charadata[i].kind; //☆3文字の場合はint型に変更
		switch (c) {
		case '111': //☆
			DrawGraph(charadata[i].x, charadata[i].y, im.kabe01, TRUE);
			break;
		case 'AAA': //☆
			int a = g_anime_counter / 20 % charadata[i].anime_pattern;
			DrawGraph(charadata[i].x, charadata[i].y, im.teki0A[a], TRUE);
			break;
		}
		charadata[i].x = charadata[i].x - 1;
	}
}

int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC)
{
	if (ChangeWindowMode(TRUE) != 0) return -1;
	if (DxLib_Init() != 0) return -1;
	if (SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;

	MapLoad();
	Load();
	Init();

	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
		if (ClearDrawScreen() != 0) return -1;
		Move();
		g_anime_counter++;
		if (ScreenFlip() != 0) return -1;
	}

	if (DxLib_End() != 0) return -1;
	return 0;
}
 私は、上記のソースコードを使いこなせれるようにはなりました。ただ、自分の文章で説明するまでには及んでいません。
 mapdata[y][x] = p[0]<<8 | p[1];とmapdata[y][x] = buf<<8 | buf[i+1];におけるビット演算子を用いた場合については、必要な分だけビット数をシフトさせる、「|」は、ビット単位の論理和を生成する演算子で、これによって文字のビット情報を調整する?くらいの記述しか出来ません。
 mapdata[y][x] = buf*256 + buf[i+1];については、2進数でデータが管理されている事を忘れてはならない。。2bitシフトすると2の2乗で4。8bixシフトすると2の8乗で256。16bitシフトで256×256乗くらいの記述しか出来ません。

 本トピックの主題である、メモ帳、改め、「メモ帳などで作成したテキストエディターからカンマ付のデータを読み込む」については、一応、解決のめどが立ちました。
 長くなったので、次回にそのソースコードなどを投稿する予定です。
添付ファイル
map4.txt
3文字の場合
(2.36 KiB) ダウンロード数: 615 回
map3.txt
1文字の場合
(1.18 KiB) ダウンロード数: 569 回

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#48

投稿記事 by かずま » 6年前

プログラマ見習い さんが書きました:
6年前
 mapdata[y][x] = buf[ i]*256 + buf[i+1];については、2進数でデータが管理されている事を忘れてはならない。。2bitシフトすると2の2乗で4。8bixシフトすると2の8乗で256。16bitシフトで256×256乗くらいの記述しか出来ません。
2進数は無関係です。
buf[i+1] が 0~255 の 256 種類の値を持つから、256倍しているだけです。

例えば、0~59 の 60種類の値を持つ 2つの数 m と s があったとします。
2数のままだと、その値の組をある特定の値、例えば (8, 15) と比較したい場合、
if (m == 8 && s == 15) と書かないといけません。
t = m*60 + s として、1つの数にすると、if (t == 8*60 + 15) と書けます。
8*60 + 15 は、定数式だから、case 8*60 + 15: と書くこともできます。

60 は 2のべき乗ではありません。

t = m<<6 | s; だと m や s が 6ビットの数だから、と 2進数を意識します。
この場合は、t = m*64 + s; と同じです。

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#49

投稿記事 by プログラマ見習い » 6年前

 予告通り、本トピックの主題である、メモ帳、改め、「メモ帳などで作成したテキストエディターからカンマ付のデータを読み込む」の解決策について投稿します。

 皆さんのアドバイスや書籍やネットを何度か読み返して、自分が使いこなせそうな解決策を選びました。

 以下にソースコードを投稿します。
 添付ファイルはmap1.txt map2.txtです。
 画像は、以前に投降したjpg形式の添付ファイル#2にあります。

 一つは解決策1で、整数のみを利用する場合です。

解決策1 map1.txt

コード:

#include <DxLib.h>

#define MAPSIZE 32

#define HEIGHT 15
#define WIDTH  40

#define CHARA (HEIGHT * WIDTH)

struct ImageData {
	int kabe01;
	int teki0A[2];
};

struct ImageData im;

struct CharaData {
        int kind; //☆
	int anime_pattern;
	int x, y;
	int body;
};
int counter;

struct CharaData charadata[CHARA];
unsigned int g_anime_counter = 0;

int mapdata[HEIGHT][WIDTH];

void MapLoad()
{
	ZeroMemory(charadata, sizeof(charadata));

	char buf[1024];
	  //strcpy_s(buf, "media/map%d.txt",1);
        sprintf_s(buf, "media/map%d.txt",1); //☆s_printf_s関数に変更。
	int fh = FileRead_open(buf);
	if (fh == NULL) { printfDx("ファイル読み込みエラー"); return; }


//////////ここから//////////

	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fh) == -1) break;
		char *p = buf;                          // ★
		for (int x = 0; x < WIDTH; x++) {       // ★
                        mapdata[y][x] = strtol(p, &p, 10); // ★ ☆10進数。
                         //mapdata[y][x] = strtol(p, &p, 16); //☆16進数の場合
			p++; // skip ','                    // ★ ☆スキップ
		}                                       // ★
	}

//////////ここまで//////////

	FileRead_close(fh);
}

void Load()
{
	im.kabe01 = LoadGraph("media/kabe01.jpg"); //☆今回は画像形式をpngにする。
	LoadDivGraph("media/teki0A.jpg", 2, 2, 1, 32, 32, im.teki0A);
}

void Init()
{
	counter = 0;
	for (int y = 0; y < HEIGHT; y++) {
		for (int x = 0; x < WIDTH; x++) {

                        //int c=mapdata[y][x]; //☆unsignedなしでも描画された。
                        unsigned int c=mapdata[y][x]; //☆
			charadata[counter].kind = c;
			charadata[counter].x = x * MAPSIZE;
			charadata[counter].y = y * MAPSIZE;
			switch (c) {
                          case 01: // ★ ☆10進数。16進数の場合は0xを前に付ける。
				charadata[counter].body = 1;
				mapdata[y][x] =00;
				counter++;
				break;
                           case 11: // ★ ☆10進数。16進数の場合は0xを前に付ける。
				charadata[counter].anime_pattern = 2;
				charadata[counter].body = 1;
                                mapdata[y][x]=00;
				counter++;
				break;
			}
		}
	}
}

void Move()
{
	for (int i = 0; i < counter; i++) {
                //int c=charadata[i].kind;  //☆unsignedなしでも描画された。
                unsigned int c=charadata[i].kind;  //☆

		switch (c) {
                 case 01: //★☆10進数。16進数の場合は0xを前に付ける。
			DrawGraph(charadata[i].x, charadata[i].y, im.kabe01, TRUE);
			break;

                 case 11: //★☆10進数。16進数の場合は0xを前に付ける。
			int a = g_anime_counter / 20 % charadata[i].anime_pattern;
			DrawGraph(charadata[i].x, charadata[i].y, im.teki0A[a], TRUE);
			break;
		}
		charadata[i].x = charadata[i].x - 1;
	}
}

int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC)
{
	if (ChangeWindowMode(TRUE) != 0) return -1;
	if (DxLib_Init() != 0) return -1;
	if (SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;

	MapLoad();
	Load();
	Init();

	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
		if (ClearDrawScreen() != 0) return -1;
		Move();
		g_anime_counter++;
		if (ScreenFlip() != 0) return -1;
	}

	if (DxLib_End() != 0) return -1;
	return 0;
}


 上記は、strtol関数に魅力を感じた事が主な理由です。
 かずまさんがstrtol関数について詳細に解説してくれたおかげで、strtol関数や、コンピュータ内部で行われている処理について、理解を深める事が出来ました。本当にありがとうございます。



 次は解決策2で、整数の他の2ケタの文字を利用する場合です。

解決策2 map2.txt

コード:

#include <DxLib.h>

#define MAPSIZE 32

#define HEIGHT 15
#define WIDTH 40

#define CHARA (HEIGHT * WIDTH)

struct ImageData {
	int kabe01;
	int teki0A[2];
};

struct ImageData im;

struct CharaData {
	short kind; // ★ short: 256種類以上格納するため
        //int kind; //☆int型でも可。但し、合わせる事。
	int anime_pattern;
	int x,y;
	int body; //体力
};

struct CharaData charadata[CHARA];
int counter; // ★ CHARA個のうち実際には counter個を使用

unsigned int g_anime_counter = 0;

short mapdata[HEIGHT][WIDTH]; // ★ short: 256種類以上格納するため
//int mapdata[HEIGHT][WIDTH]; //☆int型でも可。但し、合わせる事。

void MapLoad()
{
	char buf[1024];
	sprintf_s(buf, 1024, "media/map%d.txt", 2);
	int fp = FileRead_open(buf);  // ★ fp はファイルハンドル
	if (fp == 0) { printfDx("ファイル読み込みエラー"); return; }
/////////////////////////////ここから////////////////////////////
	for (int y = 0; y < HEIGHT; y++) {
		if (FileRead_gets(buf, 1024, fp) == -1) break;
		char *p = buf;
		for (int x = 0; x < WIDTH; x++) {
			mapdata[y][x] = p[0]<<8 | p[1]; // ★ 先頭文字が short の上位バイト
 //☆ <<はビット単位のシフト演算子。|はビット単位の論理和を生成する演算子。
			p += 3; //☆','をスキップ
		}
	}
/////////////////////////////ここまで////////////////////////////
	FileRead_close(fp);
}

void Load()
{
	im.kabe01 = LoadGraph("media/kabe01.jpg");
	LoadDivGraph("media/teki0A.jpg", 2, 2, 1, 32, 32, im.teki0A);
}

void Init()
{
	ZeroMemory(charadata, sizeof(charadata));  // ★ ここで初期化が適切
	counter = 0;
	for (int y = 0; y < HEIGHT; y++) {
		for (int x = 0; x < WIDTH; x++) {
			short c = mapdata[y][x];	// ★ short
                        //int c=mapdata[y][x]; //int型でも可。但し、合わせる事。
			charadata[counter].kind = c;
			charadata[counter].x = x * MAPSIZE;
			charadata[counter].y = y * MAPSIZE;
			switch (c) {
			case '11':
				charadata[counter].body = 1;
                                mapdata[y][x] ='00'; //
				counter++;
				break;
			case 'AA':
				charadata[counter].anime_pattern = 2;
				charadata[counter].body = 1;
                                mapdata[y][x] ='00';
				counter++;
				break;
			}

		}
	}
}

void Move()
{
	for (int i = 0; i < counter; i++) {  // ★ counter個を描画
		short c = charadata[i].kind; // ★ short
                //int c=charadata[i].kind; //☆int型でも可。但し、合わせる事。
		switch (c) {
		case '11':
			DrawGraph(charadata[i].x, charadata[i].y, im.kabe01, TRUE);
			break;
		case 'AA':
			int a = g_anime_counter / 20 % charadata[i].anime_pattern;
			DrawGraph(charadata[i].x, charadata[i].y, im.teki0A[a], TRUE);
			break;
		}
		charadata[i].x = charadata[i].x - 1;
	}
}

int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC)
{
	if (ChangeWindowMode(TRUE) != 0) return -1;
	if (DxLib_Init() != 0) return -1;
	if (SetDrawScreen(DX_SCREEN_BACK) != 0) return -1;

	MapLoad();
	Load();
	Init();

	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
		if (ClearDrawScreen() != 0) return -1;
		Move();
		g_anime_counter++;
		if (ScreenFlip() != 0) return -1;
	}

	if (DxLib_End() != 0) return -1;
	return 0;
}
 結果として、今回の解決策では、両者とも、かずまさんのアドバイスを選ぶ事になりました。出来る事なら、他の皆さんのも選びたかったのですが、残念ながら、使いこなせませんでした。

 本トピックでの私の質問については、上記のソースコードを以て、解決とさせていただきます。
 自分の中では理解が深まったとは思っていても、自分では気づいていない理解し間違いがあるのではないかと言う不安、sscanf_sを利用した場合などの他の方法も試してみたい、また、上記の解決策を今の自分では文章で表現するのが難しいなど、心残りが無きにしも非ずです。
 しかし、これ以上本トピックをを長引かせるわけにもいかない事、これ以上皆さんに負担をかけてしまう事に申し訳なく思っている事、深入りしすぎる所があった事、本トピックの趣旨から逸れる分野に及びかねない部分があった事、そして、当初の課題である「メモ帳などで作成したテキストエディターからカンマ付のデータを読み込む」ためのソースコードを使いこなす事には成功している事が主な理由です。

 上記の通り、最終的にかずまさんのアドバイスに偏ったアドバイスを選んだのですが、他の皆さんのアドバイスもまた、私が今まで理解が足りなかったプログラミングについての理解を深める事に繋がりました。
 皆さん、本当にありがとうございます。
添付ファイル
map1.txt
解決策1
(1.77 KiB) ダウンロード数: 531 回

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#50

投稿記事 by プログラマ見習い » 6年前

map2.txtの添付に失敗したので、添付します。
添付ファイル
map2.txt
解決策2
(1.77 KiB) ダウンロード数: 556 回

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#51

投稿記事 by かずま » 6年前

プログラマ見習い さんが書きました:
6年前
 予告通り、本トピックの主題である、メモ帳、改め、「メモ帳などで作成したテキストエディターからカンマ付のデータを読み込む」の解決策について投稿します。
メモ帳はテキストエディタを作成しません。
メモ帳はテキストエディタです。
テキストエディタはテキストファイルを作成します。
今回の問題にメモ帳は無関係です。

「ファイルから、カンマ付きのデータを読み込む」となぜ言わないのでしょうか?

コード:

		switch (c) {
                 case 01: //★☆10進数。16進数の場合は0xを前に付ける。
			DrawGraph(charadata[i].x, charadata[i].y, im.kabe01, TRUE);
01 は 10進数ではありません。
C のソースプログラムでは、0 で始まる数は 8進数です。
00, 01, 02, ... 07 までは書けますが、08, 09 はエラーです。

ソースの修正部分のインデント(字下げ) が変です。

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#52

投稿記事 by プログラマ見習い » 6年前

かずまさん。
ご指摘、ありがとうございます。危うく、間違ったまま理解してしまう所でした。

「ファイルから、カンマ付きのデータを読み込む」について。
すみません。初歩的な間違いをしていました。メモ帳で書いていたので、いまだにメモ帳に気を取られていた事が原因です。「ファイルから、カンマ付きのデータを読み込む」に変更します。

「01 は 10進数ではありません。C のソースプログラムでは、0 で始まる数は 8進数です。00, 01, 02, ... 07 までは書けますが、08, 09 はエラーです。」について。
驚きです。そこは初めて知りました。手元にある入門書を読み返しても、そこまでは書いてありませんでした。ネットで検索すると、ありました。01,02だけでなく、8進数の12はC言語では012と表記する事も分かりました。
となると、解決策1のコンセプトでは、ファイルのデータの見た目じょうの問題をどうするかの問題が出てきます。
と言うのも、1~9までは01~09、2ケタなら10~と、2ケタでまとめた方が見た目が見やすいのですが、見た目を重視すると、16進数以外に選択肢がなくなります。16進数に合わせて、訂正します。

「ソースの修正部分のインデント(字下げ) が変です。」について。
私は、半角1個分の方が、横スクロールバーを動かす手間が少なくて済むので、使いやすい感覚があります。インテンドが不自然なのは、コピペと修正を繰り返した結果です。
ただ、インテンド調整に関する認識が自分には不足しているとしても、当の自分にはまだピンと来ていないと言うのが本音です。
もう少し、詳しい説明をお願いします。

「ファイルから、カンマ付きのデータを読み込む」事に気を取られていたとは言え、初歩的な間違いをたびたび起こしてしまって、皆さん、本当に申し訳ございません。

かずま

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#53

投稿記事 by かずま » 6年前

プログラマ見習い さんが書きました:
6年前
「01 は 10進数ではありません。C のソースプログラムでは、0 で始まる数は 8進数です。00, 01, 02, ... 07 までは書けますが、08, 09 はエラーです。」について。
驚きです。そこは初めて知りました。手元にある入門書を読み返しても、そこまでは書いてありませんでした。ネットで検索すると、ありました。01,02だけでなく、8進数の12はC言語では012と表記する事も分かりました。
となると、解決策1のコンセプトでは、ファイルのデータの見た目じょうの問題をどうするかの問題が出てきます。
と言うのも、1~9までは01~09、2ケタなら10~と、2ケタでまとめた方が見た目が見やすいのですが、見た目を重視すると、16進数以外に選択肢がなくなります。16進数に合わせて、訂正します。
0で始まるのが 8進数なのは、C のソースコード上だけの話です。
case 09: や n = 09; などはエラーです。

しかし、プログラム内部の文字列データは、0 で始まろうが、0 が無かろうが
指定した進数で処理を行います。

n = strtol("09", NULL, 10); とあった場合、n は 9 になります。

n = 012; だと n の値は 10 ですが、
n = strtol("012", NULL, 10); の場合、n の値は 12 になります。
n = strtol("012", NULL, 16); の場合、n の値は 18 になります。
n = strtol("012", NULL, 8); の場合、n の値は 10 になります。

scanf("%d", &n); で入力が 012 でも n には 12 が入ります。

n = strtol("012", NULL, 0); のように基数に 0 を指定すると、
数字の列が 0 で始まっているか、0x で始まっているか、そうでないかで
8進数、16進数、10進数だと解釈します。

scanf も %d は 10進、%x は 16進、%o は 8進の入力ですが、
%i だと、入力の先頭の 0 または 0x の有無によって異なる変換をします。

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#54

投稿記事 by プログラマ見習い » 6年前

かずまさん。
「case 09: や n = 09; などはエラーです。」
「n = strtol("09", NULL, 10); とあった場合、n は 9 になります。」
「なるほど、そういう事だったのか。」と言う思いです。
貴重なアドバイス、本当にありがとうございます。感謝しています。

プログラマ見習い
記事: 44
登録日時: 6年前

Re: メモ帳からカンマ付き文字列のデータの読み込みが出来ません。

#55

投稿記事 by プログラマ見習い » 6年前

解決策1の部分の間違い部分を訂正します。
 10進数で読み込む場合。
Init関数とMove関数のcase 01:の部分
 case 1: //01を1に訂正。
Init関数の2か所のmapdata[y][x]=00;の部分
 mapdata[y][x]=0; //00を0に訂正。

返信

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