こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

-広告-

解決済みの質問

C++におけるバイナリファイルの入出力について

VisualStudioExpress2015のC++にて、次のような記述をし、二回目に実行したところアクセス違反が発生してしましました。
内容としてはセーブデータが見つからない場合はセーブデータにクラスの情報を書き込み、セーブデータがあった場合はセーブデータからクラスの情報を読み込むというものです。

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <map>

using namespace std;

class MyClass
{
public:
MyClass();
~MyClass();

//private:
int x, y;
char *str;
map<int,string> mMap;
};

MyClass::MyClass()
{
}

MyClass::~MyClass()
{
}

void write() {
MyClass *myclass = new MyClass;
myclass->x = 10; myclass->y = 100; myclass->str = "aaaa"; myclass->mMap[10] = "bbbb";

FILE *fp1;
if (fopen_s(&fp1,"セーブデータ.dat", "wb") != 0) {//エラーが起きたらNULLを返す
return;
}
fwrite(&myclass, sizeof(myclass), 1, fp1); // SaveData_t構造体の中身を出力
fclose(fp1);//ファイルを閉じる
}

void read() {
MyClass *myclass2 = new MyClass;
FILE *fp2;
if (fopen_s(&fp2,"セーブデータ.dat", "rb") != 0) {
return;
}
fread(&myclass2, sizeof(myclass2), 1, fp2);
fclose(fp2);

printf("x=%d\ny=%d\nstr=%s\nmMap[10]=%s\n",
myclass2->x, myclass2->y,myclass2->str,myclass2->mMap[10].c_str());
}

int main() {
FILE *fp;
if (fopen_s(&fp, "セーブデータ.dat", "rb") != 0) {
write();
}
else {
read();
}

system("pause");

return 0;
}

どこをどう記述し直したら上手く動作するのでしょうか。

これを応用して、バイナリファイルの入出力を使ってゲームのセーブデータのようなものを実現しようと考えています。
そちらの方では多くの量のデータがあるので、できるだけクラスごとバイナリファイル等に保存するようにしたいのですが、もし上記の方法が無理な場合他にどのような方法があるか教えていただきたいです。

投稿日時 - 2015-11-30 21:41:27

QNo.9088628

困ってます

質問者が選んだベストアンサー

クラス(インスタンス)をそのままfwrite/freadして使うのは色々難しいと思います。

上記プログラムでいえば、read時にstr用のメモリを割り当てていなかったり、write時もmMap内部のデータを出力できていないのではと思います。

そのような目的でboost::serializationというライブラリもありますのが、これもC/C++に慣れていないと難しいかなと思います。

クラス内の変数1つずつをread/writeする処理を記述する方が簡単かと思います。

参考URL:http://d.hatena.ne.jp/osyo-manga/20100910/1284108774

投稿日時 - 2015-12-01 00:37:01

お礼

回答ありがとうございます。

やはり、fwrite/freadではまとめて書き込むには限界があるのですね…
ライブラリの方、拝見しましたが確かに自分には少し敷居が高いと感じたので、繰り返しなどを用いて一つ一つ値を書き込んでいこうかと思います。ライブラリの方は次の機会のために参考にさせていただきます。

投稿日時 - 2015-12-01 13:51:26

このQ&Aは役に立ちましたか?

3人が「このQ&Aが役に立った」と投票しています

-広告-
-広告-

回答(3)

ANo.3

ちゃんと確認はしていません
・メインでオープンしたままクローズしていない
・各関数(Read/Write)内でもオープンしてるから
 排他制御でエラーになってるとか言った話と違いますか?

投稿日時 - 2015-12-01 14:27:01

お礼

回答ありがとうございます。

確かにおっしゃる通り、ストリームの閉じ忘れではないかと思い試したところ、同じところでエラーが出てしまいました。
やはり、地道にひとつずつ変数を読み書きするしかなさそうです…

投稿日時 - 2015-12-01 22:36:17

-広告-

ANo.2

stringは可変長の文字列を扱いますから、内部に文字列データを保持していません。必要なメモリを確保して、そのポインタを保持するようになっています。
なので、sizeof(string)はデータの大きさを表していないし、fwriteでファイルに書き込んでも、正しく読み込みはできません。

char *pstr = new char[10];

strcpy(pstr, "test");
fwrite(&pstr, sizeof(pstr), 1, fp);

としても、

fread(&pstr, sizeof(pstr), 1, fp);

で読み出せるのは、書き込んだ時のpstrが指していたポインタの値であって、その時点でそのメモリが確保されている保証もなければ、内容が"test"である保証もないわけですよ。
mapも然りで、sizeofでmapが保持しているデータのサイズを取得することはできません。

あと、

> if (fopen_s(&fp, "セーブデータ.dat", "rb") != 0) {
> write();
> }

「ファイルがオープンできない=ファイルが存在しない」と判断しているんでしょうが、オープンできない状況は他にも、アクセス権がない、別のプログラムが排他モードでオーブンしている、などが普通にあります。特殊な状況なら、もっといろいろな原因が出てきます。存在するかどうかで処理を変えたいなら、そのような記述にしましょう。

投稿日時 - 2015-12-01 10:05:45

お礼

回答ありがとうございます。

ポインタをファイルに書き込んでも読み込むときにはそのポインタは使えないのですね…
ファイルが存在するかどうかはFindFirstFileのような関数を使った方がよいということでしょうか。肝に銘じておきます。

投稿日時 - 2015-12-01 14:27:59

-広告-
-広告-

あなたにオススメの質問

-広告-
-広告-