質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

1585閲覧

C++でテキストファイルのデータを三次元配列に代入するソースを作ったのですがフリーズしてしまいます

OfuSoft

総合スコア8

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2017/03/30 21:59

C++で下記のようにテキストファイルのデータを三次元配列に代入したいのですが特定の場所でフリーズしてしまいます。

テキストファイル(MapData1.txt)のデータはこのようになっております。
LAYER_1
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1

LAYER_2
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0

LAYER_3
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0

LAYER_ITEM
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0

LAYER_CHARACTER
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0

LAYER_EVENT
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0

また、コードの方はこのようになっております。

cpp

1signed char MAPLELATION::Create(int mapnum) { // マップ読み込み生成関数 2 3 char FBuffer[32]; // ファイルパス格納バッファ 4 sprintf_s(FBuffer, sizeof(char) * 32, "Material\\MapData\\MapData%d.txt", mapnum); // ファイルパス代入 5 6 int FHandle = FileRead_open(FBuffer); // ファイルオープン,ファイルハンドルにポインタ代入 7 if (FHandle == -1) exit(-0xC1); 8 9 for (char layer = 0; layer < MAP_LAYER_MAX; layer++) { 10 while (FileRead_getc(FHandle) != '\n'); // 1行改行 11 for (char y = 0; y < MAP_Y_MAX; y++) { 12 for (char x = 0; x < MAP_X_MAX; x++) { 13 char val[3] = { 0,0,0 }; // 数値バッファ 14 for (char v = 0; v < 3; v++) { 15 val[v] = FileRead_getc(FHandle); // ファイルの文字代入 ここで止まる 16 if (val[v] != ' ') val[v] = (val[v] - '0'); // 文字を数値に変換 17 else val[v] = 0; // もし空白だったら0にする 18 } 19 Square[layer][y][x] = val[0] * 100 + val[1] * 10 + val[2]; // ファイル平面データに代入 20 FileRead_getc(FHandle); // 区切り文字進める 21 } 22 } 23 if (layer != MAP_LAYER_MAX - 1) while (FileRead_getc(FHandle) != '\n'); // 1行改行 24 else FileRead_close(FHandle); // 最後のレイヤー(LAYER_EVENT)ならファイル閉じる 25 } 26 27 28 LivingMapCode = mapnum; // マップコード代入 29 30 return mapnum; 31}

どこか抜けているところがございましたら大変申し訳ありません。

誰かアドバイス等くださる方がございましたら、心から感謝申し上げます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

この論理は「一つの数値は必ず4桁」という前提で組まれています。数字部分が3桁、要素ごとに区切り文字が1文字としているからです。しかしほとんどの要素は3桁しかありません(0, ,, , 次の要素...)。このため上記のコードを動かすと、本来読むべき位置よりどんどん後ろにずれていき、ついにはテキストの末尾に達してしまう・・・という具合になってしまいます。FileRead_getcの動作が止まるのがなぜかは分かりませんがEOF(ファイル末尾)に達した後にこの関数をさらに呼び出すと呼び出し元へ帰ってこない仕様なのかも知れません・・・

さてでは要素ごとの区切り文字を読み込む処理を削除して各要素が3桁になるように調整した場合はどうでしょうか?

最初の行をスキップした後が次の3文字になるので最初の要素は、変換した数値が正しいかどうかは別として(※後述)期待通りの範囲が読めると考えられます。

0, ,,

しかし行の末尾の要素に達した時テキストデータをWindowsのメモ帳で作成したなら次のようになっているでしょう。つまり行末では少なくとも4文字になっています。

0, ,, \r, \n

さらに7x7のマトリックスの最後の要素(右下の要素)は次のようになっているでしょうね。

0, \r, \n, \r, \n, ... 次のヘッダー行

実際のテキストの中身がどうなっているかはっきりわかりませんが、Windows前提のコードであることがうかがえるため上記のような推測をしてみました。

こうした読み込み対象のテキストの桁数/要素が一定でないものを扱うなら要素を「固定幅」として考えるのではなく「読み込んだ文字そのものを解析してどこまで読み込むかを適切に切り分ける」必要があります。詳細な論理を示すのは申し訳ないですが控えます。というのは「テキストの形式を都合の良いように変更できるかどうか」によって具体的な解析論理は簡単にも面倒にもなるからです。

ちなみに「数値は必ず1桁」「改行文字は\nのみで\rは含めない」「数字の後は, または,\nまたは\n\nのみ」として数値1要素が必ず3桁になるようにするといった前提を置いて解析をなるべく単純にするという方針もありとは思います。柔軟性を欠くとはいえますがそれで充分間に合うのならこれもまたコードを単純にするための一つの手段と思います。ただし3桁にすると「メモ帳では読みずらいテキストファイルになってしまう」ことには注意しないといけませんが。


※:3桁の数値の読み込み処理について
例えば以下のデータを読み込んだとき期待は1を読み込むことですが、実際には100になってしまいますね?

1, ,,

これも「必ず数字が3桁続く」という実際のテキストの中身と異なる前提で処理をしているために起こるバグと言えましょう。

投稿2017/03/30 23:54

編集2017/03/31 00:34
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OfuSoft

2017/04/01 08:14

回答いただき有難うございます。 私は\rの存在に気づいておらず、このようなバグを引き起こしてしまいました…、自分の勉強不足を反省しております。 また、わざわざテキストについても詳しく推測して下さり、よく分かりました。
guest

0

最深部のループが3回固定で回っているのが原因でしょうか。
ファイル内の数値が全て3桁になっているのなら問題ないかもしれませんが、そうではなさそうなので、柔軟に対応できる処理が必要かと思います。
固定長ではなく、可変長に対応できるように組み直してみましたがいかがでしょうか(未検証)
Xのループのところのみ入れ替えてください。

c

1 for (char x = 0; x < MAP_X_MAX; ) { 2 char val; // 数値バッファ 3 val = FileRead_getc(FHandle); // ファイルの文字代入 ここで止まる 4 if (val >= '0' && val <= '9'){ 5 val = val - '0'; // 文字を数値に変換 6 Square[layer][y][x] = Square[layer][y][x] * 10 + val; // ファイル平面データに代入 7 } if (val == ','){ 8 x++; 9 } 10 } 11

投稿2017/03/31 00:37

ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OfuSoft

2017/04/01 08:04

回答して頂きありがとうございます。 しかし、前に可変長配列を扱ったデータ読み込みも試みてはいたのですが、私にはとても難しく、やはりメモリリークが怖いので無念ながら諦めました…
guest

0

デバッグからですね。

まずは、
while (FileRead_getc(FHandle) != '\n');
のところで、読み込んだ文字を1文字ずつ出力させます。

次に、
val[v] = FileRead_getc(FHandle);
のところも、読み込んだ文字を1文字ずつ出力させます。

入力として想定しているデータが読まれているかをまず確かめましょう。
この結果を教えて頂いて次にいけたらいいと思います。

投稿2017/03/30 23:25

takotakot

総合スコア1111

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OfuSoft

2017/04/01 08:07

アドバイス頂き有難うございます。 その方法を使い、変数の中身がめちゃくちゃになっているのが分かりました。しかしこの方法はデバッグにはとても重要だと考えます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問