配列を用いた構造体をfwriteで書き込みたい

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

配列を用いた構造体をfwriteで書き込みたい

#1

投稿記事 by bonbo » 13年前

配列を用いた構造体をfwriteでバイナリファイルに書き込み、それを読み込もうと思い、以下のようなプログラムをつくったのですが、うまく動作してくれません(読み込めていないのか書き込めていないのかわからないが、すべて0になっている)。どうすればいいのでしょうか。

コード:

#include <iostream>
using namespace std;
const int testmax=10;
typedef struct {
	int a1,a2;
}test_t;
test_t test[testmax];
inline void Save(test_t test[]){
	FILE *fp ;
	fp = fopen( "test.dat" , "wb" ) ;
	if( fp == NULL )
		return ;
	fwrite(test,sizeof(test) , 1 , fp ) ;
	// ファイルを閉じる
	fclose( fp ) ;
}
inline void Load(test_t test[]){
	FILE *fp ;
	fp = fopen("test.dat","rb") ;
	if(fp==NULL){
	}
	else
	{
		//データの読み込み
		fread(test, sizeof(test) , 1 , fp ) ;
		// ファイルを閉じる
		fclose( fp ) ;
	}
}
int main(void){
	for(int i=0;i<testmax;i++){
		test[i].a1=i;
		test[i].a2=10-i;
	}
	Save(test);
	for(int i=0;i<testmax;i++){
		test[i].a1=0;
		test[i].a2=0;
	}
	Load(test);
	for(int i=0;i<testmax;i++)
		cout<<test[i].a2<<"\n";
	cin>>test[0].a2;
	return 0;
}

box
記事: 2002
登録日時: 15年前

Re: 配列を用いた構造体をfwriteで書き込みたい

#2

投稿記事 by box » 13年前

bonbo さんが書きました:

コード:

	fwrite(test,sizeof(test) , 1 , fp ) ;
		fread(test, sizeof(test) , 1 , fp ) ;
fwrite() と fread() の第3引数が1である理由を教えてください。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

box
記事: 2002
登録日時: 15年前

Re: 配列を用いた構造体をfwriteで書き込みたい

#3

投稿記事 by box » 13年前

第3引数がどうのこうの言う前に、コンパイルが通らないっぽいですね。

# 当方だけかな?きっとそうなんでしょう。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

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

Re: 配列を用いた構造体をfwriteで書き込みたい

#4

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

Save関数とLoad関数の中のtestはtest_t型の変数へのポインタであり、
サイズは(Ideoneでは)4バイトになります。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 配列を用いた構造体をfwriteで書き込みたい

#5

投稿記事 by ISLe » 13年前

fread/fwriteで読み書きしているのは、関数の引数であるtest_tへのポインタ型の一時変数testです。
なので外部宣言されたtest配列には影響しません。


(訂正)
#思いっきり勘違い。<俺
Load/Save関数内のtestは、関数の引数であるtest_tへのポインタ型の一時変数testが対象です。
なのでsizeofではtest_tへのポインタ型のサイズが返ります。
というのは、みけCATさんが書いているとおり。
最後に編集したユーザー ISLe on 2012年8月19日(日) 23:26 [ 編集 2 回目 ]

bonbo

Re: 配列を用いた構造体をfwriteで書き込みたい

#6

投稿記事 by bonbo » 13年前

>boxさん
ありがとうございます。
>fwrite() と fread() の第3引数が1である理由を教えてください。
ミスです…というかよくわかっていませんでした…
この場合、a1の要素10こ、とa2の要素10こで20とすべきだ。ということでしょうか…間違っていたらません…
>みけCATさん
ありがとうございます。
>Save関数とLoad関数の中のtestはtest_t型の変数へのポインタであり、
サイズは(Ideoneでは)4バイトになります。
つまり関数内ではバイト数が変わるので、そのままではsizeof()の中には入れられないということでしょうか…

bonbo

Re: 配列を用いた構造体をfwriteで書き込みたい

#7

投稿記事 by bonbo » 13年前

一応第三引数を増やしたら書き込めるようにはなりました。いろいろ実験してみたところfwriteの仕組みがわかったので解決にしておきます。みなさんありがとうございました。また何かありましたらよろしくお願いします。

bonbo

Re: 配列を用いた構造体をfwriteで書き込みたい

#8

投稿記事 by bonbo » 13年前

忘れてました…

コード:

#include <iostream>
using namespace std;
const int testmax=10;
typedef struct {
	int a1,a2;
}test_t;
test_t test[testmax];
inline void Save(test_t test[]){
	FILE *fp ;
	fp = fopen( "test.dat" , "wb" ) ;
	if( fp == NULL )
		return ;
	fwrite(test,sizeof(test) , 20 , fp ) ;
	// ファイルを閉じる
	fclose( fp ) ;
}
inline void Load(test_t test[]){
	FILE *fp ;
	// オープンできなかったらファイルが無いとみなし
	//消す
	fp = fopen("test.dat","rb") ;
	if(fp==NULL){
	}
	else
	{
		//データの読み込み
		fread(test, sizeof(test) , 20 , fp ) ;
		// ファイルを閉じる
		fclose( fp ) ;
	}
}
int main(void){
	for(int i=0;i<testmax;i++){
		test[i].a1=i;
		test[i].a2=10-i;
	}
	Save(test);
	for(int i=0;i<testmax;i++){
		test[i].a1=0;
		test[i].a2=0;
	}
	Load(test);
	for(int i=0;i<testmax;i++)
		cout<<test[i].a2<<"\n";
	for(int i=0;i<testmax;i++)
		cout<<test[i].a1<<"\n";
	cin>>test[0].a2;
	return 0;
}

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

Re: 配列を用いた構造体をfwriteで書き込みたい

#9

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

bonbo さんが書きました:つまり関数内ではバイト数が変わるので、そのままではsizeof()の中には入れられないということでしょうか…
グローバルの変数名と関数の引数の名前が同じだから分かりにくいのです。
グローバル変数名 test を g_test とかにしてみましょう。
また配列を関数の引数として渡すなら、個数も渡すべきでしょう。

コード:

// 略
test_t g_test[testmax]; // 変数名はなるべく同じ名前にしない!!
inline void Save(test_t test[], int kosu)
{
	// 略
	
	// 変数名でなく型名をsizeofする
	fwrite(test,sizeof(test_t), kosu, fp );
	// 略
}

inline void Load(test_t test[], int kosu)
{
	// 略
	
	// 変数名でなく型名をsizeofする
	fread(test,sizeof(test_t), kosu, fp );
	// 略
}

// main関数内では
// Save(g_test, testmax);
// Load(g_test, testmax);
オフトピック
bonbo さんが書きました:いろいろ実験してみたところfwriteの仕組みがわかったので解決にしておきます
fread/fwriteの第3引数を20にしてて、sizeofに渡すのも変数のままなので、本当に理解しているの?とこちらでは思うのですが。
最後に編集したユーザー へにっくす on 2012年8月19日(日) 23:28 [ 編集 3 回目 ]
written by へにっくす

box
記事: 2002
登録日時: 15年前

Re: 配列を用いた構造体をfwriteで書き込みたい

#10

投稿記事 by box » 13年前

bonbo さんが書きました:

コード:

	fwrite(test,sizeof(test) , 20 , fp ) ;
		fread(test, sizeof(test) , 20 , fp ) ;
第3引数を20にした理由は何ですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

bonbo

Re: 配列を用いた構造体をfwriteで書き込みたい

#11

投稿記事 by bonbo » 13年前

>へにっくすさん
ありがとうございます。
>変数名はなるべく同じ名前にしない!!
>変数名でなく型名をsizeofする
>また配列を関数の引数として渡すなら、個数も渡すべきでしょう。
なるほど…
全然理解してないですね…親切なアドバイスありがとうございます…
>boxさん
>第3引数を20にした理由は何ですか?
この場合、a1の要素10こ、とa2の要素10こで20とすべきだ。と思ったからです。間違っていたら指摘していただけると嬉しいです

box
記事: 2002
登録日時: 15年前

Re: 配列を用いた構造体をfwriteで書き込みたい

#12

投稿記事 by box » 13年前

bonbo さんが書きました: この場合、a1の要素10こ、とa2の要素10こで20とすべきだ。と思ったからです。間違っていたら指摘していただけると嬉しいです
構造体のメンバーが増えたら、その都度30にしたり40にしたりするんですか?
何だか効率的ではないような気がします。
test_t 型(メンバーの構成は知ったことではない)が testmax 個あると考える方が、
より自然ではないのかな、という気がします。配列test[] の定義がそういう風になってますよね。
その配列をまるっと読み書きするのですから、test[] の先頭アドレスから、
test_t 型の大きさの要素を testmax 個読み書きする、というコードの方が、
改変が少なくてすむような気がします。
最後に編集したユーザー box on 2012年8月19日(日) 23:51 [ 編集 1 回目 ]
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

bonbo

Re: 配列を用いた構造体をfwriteで書き込みたい

#13

投稿記事 by bonbo » 13年前

boxさん
>test_t 型(メンバーの内容は変わりうる)が testmax 個あると考える方が、
より自然ではないのかな、という気がします。
つまり…メンバーの数をmenbernumとでもおいて
20のところをtestmax*menbernumとすればいいということでしょうか。
そのほうがきれいですね。
アドバイスありがとうございます。

box
記事: 2002
登録日時: 15年前

Re: 配列を用いた構造体をfwriteで書き込みたい

#14

投稿記事 by box » 13年前

bonbo さんが書きました: つまり…メンバーの数をmenbernumとでもおいて
20のところをtestmax*menbernumとすればいいということでしょうか。
membernum という変数を導入する必要は、ありません。
sizeof(test_t) という、構造体の大きさが testmax 個ある、ということです。
仮に、構造体の中に、double型とかchar型とかのメンバーを追加したとしても、
「え~とint型が2個でdouble型が何個でchar型が何バイトだから…」などという計算を自分でしなくても、
構造体そのもの大きさはいつだって sizeof(test_t) で求まる、というのがポイントです。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

bonbo

Re: 配列を用いた構造体をfwriteで書き込みたい

#15

投稿記事 by bonbo » 13年前

>boxさん
>sizeof(test_t) という、構造体の大きさが testmax 個ある
fwrite(test,sizeof(test_t) , sizeof(test_t)*testmax, fp ) ;
でしょうか…

bonbo

Re: 配列を用いた構造体をfwriteで書き込みたい

#16

投稿記事 by bonbo » 13年前

>仮に、構造体の中に、double型とかchar型とかのメンバーを追加したとしても、
「え~とint型が2個でdouble型が何個でchar型が何バイトだから…」などという計算を自分でしなくても、
構造体そのもの大きさはいつだって sizeof(test_t) で求まる、というのがポイントです。
なるほど!ありがとうございます!

box
記事: 2002
登録日時: 15年前

Re: 配列を用いた構造体をfwriteで書き込みたい

#17

投稿記事 by box » 13年前

bonbo さんが書きました: >sizeof(test_t) という、構造体の大きさが testmax 個ある
fwrite(test,sizeof(test_t) , sizeof(test_t)*testmax, fp ) ;
でしょうか…
それだと、
sizeof(test_t) という大きさの領域が
sizeof(test_t)*testmax 個ある、
ということになってしまいそうです。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

ISLe
記事: 2650
登録日時: 15年前
連絡を取る:

Re: 配列を用いた構造体をfwriteで書き込みたい

#18

投稿記事 by ISLe » 13年前

box さんが書きました:「え~とint型が2個でdouble型が何個でchar型が何バイトだから…」などという計算を自分でしなくても、
構造体そのもの大きさはいつだって sizeof(test_t) で求まる、というのがポイントです。
メンバのサイズの合計と構造体のサイズが等しいとは限りませんしね。

かずま

Re: 配列を用いた構造体をfwriteで書き込みたい

#19

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

Save や Load の中に 20 なんて書いてはダメです。次のようにすれば、
配列の要素数が変わっても、testmax の値の変更一か所だけで済みます。

コード:

#include <iostream>
#include <cstdio>

using namespace std;

const int testmax = 10;

struct test_t {
    int a1, a2;
};

test_t test[testmax];

inline void Save(const test_t *test, size_t size)
{
    FILE *fp = fopen("test.dat", "wb");
    if (fp == NULL) return;
    fwrite(test, sizeof(test_t), size, fp);
    fclose(fp);
}

inline void Load(test_t *test, size_t size)
{
    FILE *fp = fopen("test.dat", "rb");
    if (fp == NULL) return;
    fread(test, sizeof(test_t), size, fp);
    fclose(fp);
}

int main()
{
    for (int i = 0; i < testmax; i++) {
        test[i].a1 = i;
        test[i].a2 = 10-i;
    }
    Save(test, testmax);
    for (int i = 0; i < testmax; i++) {
        test[i].a1 = 0;
        test[i].a2 = 0;
    }
    Load(test, testmax);
    for (int i = 0; i < testmax; i++)
        cout << test[i].a2 << "\n";
    for (int i = 0; i < testmax; i++)
        cout << test[i].a1 << "\n";
    return 0;
}
さらに、C++ だったら、FILE よりも <fstream> でしょう。

コード:

#include <fstream>

using namespace std;

inline void Save(const test_t *test, size_t size)
{
    ofstream ofs("test.dat", ios::binary);
    if (ofs) ofs.write((const char *)test, sizeof(test_t) * size);
}

inline void Load(test_t *test, size_t size)
{
    ifstream ifs("test.dat", ios::binary);
    if (ifs) ifs.read((char *)test, sizeof(test_t) * size);
}

閉鎖

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