C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[6]: fread関数の使い方


(過去ログ 129 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■76629 / inTopicNo.1)  fread関数の使い方
  
□投稿者/ ゆーきゃん (26回)-(2015/07/28(Tue) 14:40:02)

分類:[C/C++] 

使用言語 C++

いつも大変お世話になっております。
質問に目を通して頂きありがとうございます。

現在、fread関数を使用しファイルの読み込みをしようと思っているのですが、
思い通りの結果が出ず、困っております。

char c1~c4までは思い通りの文字を出力できるのですが、
その後の数値がうまく出力できません。

どなたかお力添えお願い致します。
以下に作成中のプログラムを提示させて頂きます。

struct kouzou {

	char c1; 
	char c2;
	char c3;
	char c4;

	double d1;
	double d2;
	double d3;
	double d4;
	double d5;
	double d6;

	short s1;

	double d7;
	double d8;
	double d9;
	double d10;
	double d11;
	double d12;

	float f1;
	float f2;
	float f3;
	float f4;

	short s2;

	float f5;
	float f6;

	short s3;

	float f7;
};


void test()
{
	struct kouzou b_d;

	FILE *fpr = fopen("ex.Dat", "rb");

	fread(&b_d, sizeof(b_d), 1, fpr);

	fclose(fpr);

	printf("c1 = %c\n", c1);
	printf("c2 = %c\n", c2);
	printf("c3 = %c\n", c3);
	printf("c4 = %c\n", c4);

	printf("d1 = %lf\n", d1);
	・
    ・
    ・
	printf("d6 = %lf\n", d6);

	printf("s1 = %d\n", s1);
	
        ・
    ・
    ・
    ・
    ・
	
	printf("f7 = %d\n", f7);

}


ex.dat-----------------------------------------

42 20 20 20 00 00 00 00 00 C0 72 40 00 00 00 00 
00 C0 72 40 00 00 00 00 00 00 12 40 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 02 00 00 00 00 00 00 40 8F 40 00 00 
00 00 00 40 8F 40 00 00 00 00 00 00 00 00 00 00 
00 00 00 40 8F 40 00 00 00 00 00 40 8F 40 00 00 
00 00 00 40 8F 40 00 00 80 3F 00 00 80 3F 00 00 
00 00 00 00 00 00 15 00 00 00 7F 43 00 00 00 00 
01 00 00 00 00 00

------------------------------------------------

引用返信 編集キー/
■76630 / inTopicNo.2)  Re[1]: fread関数の使い方
□投稿者/ 774RR (282回)-(2015/07/28(Tue) 14:59:01)
どうなることが期待通りなのか読者には判らないので、一般論しか提供できないよ。

struct には padding (詰め物) を途中に入れることができる
padding の入れ方はコンパイラによって・同一コンパイラでもコンパイルオプションによって違う
int や double の「バイト順」は CPU によって違う
ので、バイナリファイルの入出力に fread/fwrite を使うと移植性がなくなっちゃう。

fread/fwrite の勉強のためなら問題ないけど
実用に供するなら fread/fwrite で struct の入出力はしないほうが良い。

引用返信 編集キー/
■76634 / inTopicNo.3)  Re[2]: fread関数の使い方
□投稿者/ ゆーきゃん (28回)-(2015/07/28(Tue) 16:11:14)
No76630 (774RR さん) に返信
> どうなることが期待通りなのか読者には判らないので、一般論しか提供できないよ。
> 
> struct には padding (詰め物) を途中に入れることができる
> padding の入れ方はコンパイラによって・同一コンパイラでもコンパイルオプションによって違う
> int や double の「バイト順」は CPU によって違う
> ので、バイナリファイルの入出力に fread/fwrite を使うと移植性がなくなっちゃう。
> 
> fread/fwrite の勉強のためなら問題ないけど
> 実用に供するなら fread/fwrite で struct の入出力はしないほうが良い。
> 

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

fread/fwriteを使用する際は、

size_t fread(void *buf, size_t size, size_t n, FILE *fp); 

第一引数には格納先のバッファ
第二引数は読み込むデータ1つのバイト数
第三引数は読み込むデータの個数
第四引数はファイルポインタ

の第二引数にstruct型の構造体を設置すると移植性がなくなってしまうということでしょうか?


申し訳ございません。
どうなることが期待通りなのか記載させて頂きました。

今現在プログラムを走らすと以下のようなprint結果が出力されます。

c1 = B
c2 =
c3 =
c4 =
d1 = 0.000000(5.342144478788e-315#DEN)※ double d1 に格納されていた値
d2 = 0.000000(5.342144478788e-315#DEN)※ double d2 に格納されていた値
d3 = 0.000000(5.310817712923e-315#DEN)※ double d3 に格納されていた値
d4 = 0.000000
d5 = 0.000000
d6 = 0.000000

欲しい結果は以下となります。

c1 = B
c2 =
c3 =
c4 =
d1 = 300.0
d2 = 300.0
d3 = 4.5
d4 = 0.000000
d5 = 0.000000
d6 = 0.000000

char型は思い通り出力できているのですが、double型になると値がうまく読み込むことができません。

char型は1バイトずつ読み込むので、「c1=42、c2=20、c3=20、4=20」となり、
ASCIIコードより、「c1=B、c2=空白、c3=空白、c4=空白」となると思います。
続きをdouble型で読み込むと、「00 00 00 00 00 C0 72 40」より、「300」が出力されるようにしたいです。
何かコードとかの指定など必要になってくるのでしょうか?

言語を勉強し始めて間もないため、この説明で伝わるか不安ではありますがよろしくお願い致します。




引用返信 編集キー/
■76635 / inTopicNo.4)  Re[3]: fread関数の使い方
□投稿者/ ゆーきゃん (29回)-(2015/07/28(Tue) 16:19:26)
必要な情報かわかりませんが、補足させて頂きます。

「00 00 00 00 00 C0 72 40」が「300」というのは、
書き込みテストをした際の結果を持ってきています。

struct kouzou {

     double d1;
}

int main(void) {

	struct kouzou b_d[] = {
                    
	        { 300 }
        }; 

        FILE *fpw = fopen("ex.Dat", "wb");

	fwrite(&b_d, sizeof(b_d), 1, fpw);

	fclose(fpw);

	return 0;
}

ex.dat-----------------------------------------

00 00 00 00 00 C0 72 40

-----------------------------------------------

という書き込みが、ファイルにされました。 



引用返信 編集キー/
■76636 / inTopicNo.5)  Re[3]: fread関数の使い方
□投稿者/ 774RR (284回)-(2015/07/28(Tue) 16:37:52)
> の第二引数にstruct型の構造体を設置すると移植性がなくなってしまうということでしょうか?
第一引数だけど、そのとおり。

移植性という言葉にはレベルがあって
Lv1 同一ソースコードを異処理系でコンパイルでき、実行できる
Lv2 異処理系で動作させたプログラムが生成するデータファイルが互換
この場合 Lv1 は確保できているけど Lv2 は保証無し、ってことだ。

提示 ex.dat を生成したのはあるコンパイラによって作られたプログラムで
今回 ex.dat を読み込むのは別のコンパイラ (書いてないけど何だろう?) によって作られたプログラムで
先の指摘どおりデータファイルは互換でない、という状況に陥っているのだろう。

この場でオイラが説明してもいいんだけど google 先生に訊いてみてもいいだろう。
検索キーワード fwrite パディング
https://www.google.co.jp/?gws_rd=ssl#q=fwrite+%E3%83%91%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0
ヒットしたどのページを見ても参考になるはず。

さて、ではどう読み込んだら良いか・・・は、何をどうしたいのか次第。
・既に ex.dat のファイルフォーマットが決定済みで変更できないのか
・読み込む側で都合の良いように ex.dat のファイルフォーマットを変更してよいのか
・そもそもバイナリ形式でなきゃならないのか、テキスト形式に変更できないか?
あたりで実装は変わってくるよ。

引用返信 編集キー/
■76637 / inTopicNo.6)  Re[4]: fread関数の使い方
□投稿者/ ゆーきゃん (30回)-(2015/07/28(Tue) 18:32:17)
No76636 (774RR さん) に返信
>>の第二引数にstruct型の構造体を設置すると移植性がなくなってしまうということでしょうか?
> 第一引数だけど、そのとおり。
> 
> 移植性という言葉にはレベルがあって
> Lv1 同一ソースコードを異処理系でコンパイルでき、実行できる
> Lv2 異処理系で動作させたプログラムが生成するデータファイルが互換
> この場合 Lv1 は確保できているけど Lv2 は保証無し、ってことだ。
> 
> 提示 ex.dat を生成したのはあるコンパイラによって作られたプログラムで
> 今回 ex.dat を読み込むのは別のコンパイラ (書いてないけど何だろう?) によって作られたプログラムで
> 先の指摘どおりデータファイルは互換でない、という状況に陥っているのだろう。
> 
> この場でオイラが説明してもいいんだけど google 先生に訊いてみてもいいだろう。
> 検索キーワード fwrite パディング
> https://www.google.co.jp/?gws_rd=ssl#q=fwrite+%E3%83%91%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0
> ヒットしたどのページを見ても参考になるはず。
> 
> さて、ではどう読み込んだら良いか・・・は、何をどうしたいのか次第。
> ・既に ex.dat のファイルフォーマットが決定済みで変更できないのか
> ・読み込む側で都合の良いように ex.dat のファイルフォーマットを変更してよいのか
> ・そもそもバイナリ形式でなきゃならないのか、テキスト形式に変更できないか?
> あたりで実装は変わってくるよ。
> 

分かりやすいご説明ありがとうございます。
パディングという言葉を初めて聞きまして、提示して頂いたURLを元に調べて参りました。

パティングについてちょっと整理したいのですがお付き合い願いますでしょうか?

ex.Dat(パティングなし)--------------------------

42 20 20 20 00 00 00 00 00 C0 72 40 00 00 00 00 
00 C0 72 40 00 00 00 00 00 00 12 40 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 02 00 00 00 00 00 00 40 8F 40 00 00 
00 00 00 40 8F 40 00 00 00 00 00 00 00 00 00 00 
00 00 00 40 8F 40 00 00 00 00 00 40 8F 40 00 00 
00 00 00 40 8F 40 00 00 80 3F 00 00 80 3F 00 00 
00 00 00 00 00 00 15 00 00 00 7F 43 00 00 00 00 
01 00 00 00 00 00
-----------------------------------------------


ex.Dat(パティングあり)-------------------------

42 20 20 20 CC CC CC CC 00 00 00 00 00 C0 72 40
00 00 00 00 00 C0 72 40 00 00 00 00 00 00 12 40 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 02 00 CC CC CC CC CC CC 
00 00 00 00 00 40 8F 40 00 00 00 00 00 40 8F 40 
00 00 00 00 00 00 00 00 00 00 00 00 00 40 8F 40 
00 00 00 00 00 40 8F 40 00 00 00 00 00 40 8F 40 
00 00 80 3F 00 00 80 3F 00 00 00 00 00 00 00 00 
15 00 CC CC 00 00 7F 43 00 00 00 00 01 00 CC CC 
00 00 00 00 CC CC CC CC

-----------------------------------------------
※ CC = パティング(詰め物)

自分が提示させて頂いたデータを例で挙げますと、
一番バイト数が大きいdouble型を基準にパティングし、
char型・int型・short型だった際は、8バイトになるまで
詰め物を詰め、8バイトにし、8バイト毎に読み込んでいく
という解釈でよろしいでしょうか?


1・既に ex.dat のファイルフォーマットが決定済みで変更できないのか
2・読み込む側で都合の良いように ex.dat のファイルフォーマットを変更してよいのか
3・そもそもバイナリ形式でなきゃならないのか、テキスト形式に変更できないか?

という問題に移っていきますが、

3はできることなら避けたいです。

1はパティングを加えられない状態で読み込むのか。
2はパティングを加えて読み込むのか。

という解釈であっていますでしょうか。

この解釈でよろしいようでしたら、1・2どちらでも構わない状態です。

この場合ですと、上で提示させて頂いたプログラムにソースを書き足すことで対応可能でしょうか?

だいぶ長くなってしまいましたが、ご指導ご鞭撻の程よろしくお願い致します。


引用返信 編集キー/
■76638 / inTopicNo.7)  Re[4]: fread関数の使い方
□投稿者/ 774RR (285回)-(2015/07/28(Tue) 19:47:07)
最初に書いて欲しいんだけど、処理系(コンパイラ)は何? Visual Studio 2013 Community とかそういう奴

padding の話はコンパイラ・コンパイルオプションによって違うので
> 一番バイト数が大きいdouble型を基準にパティングし、
> char型・int型・short型だった際は、8バイトになるまで
> 詰め物を詰め、8バイトにし、8バイト毎に読み込んでいく
のは、今お使いのコンパイラ+コンパイルオプションでは Yes としか言えない。
まあよくある、きわめて順当で扱いやすい動作だと思う。

padding の方式は
Visual Studio 系なら -Zp コンパイルオプションで変更できる
https://msdn.microsoft.com/ja-jp/library/ms253935.aspx
gcc 系なら __attribute__((__packed__)) で変更できる

あとバイナリ形式で入出力する際には endianess も重要だよ。 300.0 を
00 00 00 00 00 C0 72 40 とメモリ中に保持するのか
40 72 C0 00 00 00 00 00 とメモリ中に保持するのか
の違い。これを fwrite/fread で直接入出力すると順番が違ってしまう。

----

> 1はパティングを加えられない状態で読み込むのか。
> 2はパティングを加えて読み込むのか。
いやそうではなく、たとえば zip や rar や lzh のように既にファイルのフォーマットが定められていて
それを守らないと使えない/使う意味が無いのかどうか、だ。
ファイルの形式を今から決めるのであれば padding を入れるも入れないも、自由に決めればよい。

オイラならこう考える
質問. そのソースコードを異処理系でコンパイルして同一ファイルが入出力できる必要があるか?
Yes → padding/endianess が違ってもファイル内容が同一になるよう工夫して書く
No → padding/endianess を決めウチして go
どちらにせよファイルのフォーマットはきっちり文書化しておく。 padding/endianess を明記。

データファイル非互換 padding ありでよいのなら 最初の発言 76629 のままで良いわけだ。
ex.dat を手書きするのではなくて、先に構造体を fwrite してみると良かったんだね。

引用返信 編集キー/
■76642 / inTopicNo.8)  Re[5]: fread関数の使い方
□投稿者/ ゆーきゃん (31回)-(2015/07/29(Wed) 09:26:18)
No76638 (774RR さん) に返信

おはようございます。
見当違いな発言をしてしまい申し訳ないです。


> 最初に書いて欲しいんだけど、処理系(コンパイラ)は何? Visual Studio 2013 Community とかそういう奴

俗にいう環境というやつでしょうか。
visual studio professional 2013 を使用しており、プロジェクトはwindowsフォームアプリケーションで作成しております。
.cppファイルにプログラムソースを書き込み作業しております。
 
 
> padding の方式は
> Visual Studio 系なら -Zp コンパイルオプションで変更できる
> https://msdn.microsoft.com/ja-jp/library/ms253935.aspx
> gcc 系なら __attribute__((__packed__)) で変更できる
> あとバイナリ形式で入出力する際には endianess も重要だよ。 300.0 を
> 00 00 00 00 00 C0 72 40 とメモリ中に保持するのか
> 40 72 C0 00 00 00 00 00 とメモリ中に保持するのか
> の違い。これを fwrite/fread で直接入出力すると順番が違ってしまう。
> 

提示して頂いたURLを元にもう少しpadding方式について調べさせて頂きたいと思います。
endianessという言葉も初めてお目にかかりました。
fwrite/fread を使用した際、「 00 00 00 00 00 C0 72 40 」固定だと思っておりました。条件によっては「40 72 C0 00 00 00 00 00 」にもなりうるということですね。こちらも少し調べて参ります。


> いやそうではなく、たとえば zip や rar や lzh のように既にファイルのフォーマットが定められていて
> それを守らないと使えない/使う意味が無いのかどうか、だ。
> ファイルの形式を今から決めるのであれば padding を入れるも入れないも、自由に決めればよい。

失礼いたしました。
フォーマットは決められております。
Datファイル形式(ex.Dat)のものを読み込まなくてはなりません。
さらにpaddingなしで詰められているデータなので、読み込むのに苦戦している次第です。

自分が上で掲載させて頂いたプログラムではpaddingありきのファイルでしたら読み込めますが、paddingなしのファイルですと読み込みバイト数がズレてうまく読み込めない状態だと思います。なので、paddingなしのファイルを読み込めるよう改良したいのです。

上で掲載したプログラムのように、単純に fwrite/fread で読み書きしてしまうと、paddingありで読み書きしてしまうので、これをpaddingなしで読み書きできるようにしたいと考えております。

至らない点が多々あると思いますが、もう一度ご指導ご鞭撻の程よろしくお願い致します。

引用返信 編集キー/
■76643 / inTopicNo.9)  Re[6]: fread関数の使い方
□投稿者/ ゆーきゃん (32回)-(2015/07/29(Wed) 09:48:29)
連投失礼いたします。

先程の件ですが、構造体の前後に #pragma pack(1)を加えることで、読み書き共にパティングを消すことができました。

#pragma pack(1)
 // ここで構造体宣言
#pragma pack()

一応これで思い通りの結果を出力することに成功したのですが、何かこのコマンドを使う際の注意点など御座いますでしょうか?

引用返信 編集キー/
■76644 / inTopicNo.10)  Re[6]: fread関数の使い方
□投稿者/ 774RR (286回)-(2015/07/29(Wed) 10:15:33)
何度も「異処理系」と言っているわけだけど、世の中には Windows でも Linux でもないマシンがある。
そういうマシン上で同一ソースコードをコンパイルし同一データファイルが生成できる必要があるか?
それとも x64/x86 のみ考えてそれ以上の移植性は必要ないと切り捨てるか?
の問題なわけだ。

Windows 陣営は x86/x64 で、これは little endian
Linux 陣営は x86/x64/ARM で、これも little endian
それ以外の一部マイコンや商用 UNIX では big endian
bigendian 勢を切り捨ててしまってよいか?は、ゆーきゃん氏なり上司なりの判断になるだろう。

さて・・・とりあえず切り捨ててしまってよいのなら話は簡単。
Visual Studio 2013 なら (Form App ってことは C++/CLI なんだろうか?)

案1 #pragma pack(1) とすると padding をやめさせることができる
https://msdn.microsoft.com/ja-jp/library/ms253935.aspx

#pragma pack(1)
struct kouzou { ... };
#pragma pack()
とすることで期待通りの入出力ができるだろう。

pros ソースコードが簡単
cons この kouzou 変数を使った演算は超遅くなる可能性がある

案2 #pragma pack(1) したものとしてないものの2つの kouzou を用意する
#pragma pack(1)
struct kouzou_packed { ... };
#pragma pack
struct kouzou_aligned { ... };

計算処理は kouzou_aligned で行う(padding あり、つまりメンバアクセスが高速)
fwrite の際に kouzou_aligned から kouzou_packed にメンバごとコピーを行う
fwrite は kouzou_packed で行う
fread は逆

pros kouzou 変換処理が入る程度でソースコードはさほど難しくない
cons せっかく変換処理を入れてるのにデータファイル互換性は無い

案3 endianess/packing を考慮しながら「メンバごとの fwrite/fread を行う」
pros データファイル互換なソースコードとなる
cons ソースコードとしては複雑になる

ってとこだろう


引用返信 編集キー/
■76645 / inTopicNo.11)  Re[7]: fread関数の使い方
□投稿者/ ゆーきゃん (33回)-(2015/07/29(Wed) 11:30:45)
No76644 (774RR さん) に返信
> 何度も「異処理系」と言っているわけだけど、世の中には Windows でも Linux でもないマシンがある。
> そういうマシン上で同一ソースコードをコンパイルし同一データファイルが生成できる必要があるか?
> それとも x64/x86 のみ考えてそれ以上の移植性は必要ないと切り捨てるか?
> の問題なわけだ。

何度も同じことを書かせてしまい申し訳ございませんでした。
異処理系というのはwindowsやlinux以外のマシンのことで、例えば独自OSやロボットなどに私が書いたプログラムソースを渡し、データファイルが生成できる必要があるのかという解釈でよろしいでしょうか?

今回、windowsのみで対応させる予定でいるため、「 Windows 陣営は x86/x64 で、little endian 」のみに当てはまるため、それ以外の条件に対しては切り捨てることになると思います。

 
> 案1 #pragma pack(1) とすると padding をやめさせることができる
> https://msdn.microsoft.com/ja-jp/library/ms253935.aspx
> 
> #pragma pack(1)
> struct kouzou { ... };
> #pragma pack()
> とすることで期待通りの入出力ができるだろう。
> 
> pros ソースコードが簡単
> cons この kouzou 変数を使った演算は超遅くなる可能性がある
> 
> 案2 #pragma pack(1) したものとしてないものの2つの kouzou を用意する
> #pragma pack(1)
> struct kouzou_packed { ... };
> #pragma pack
> struct kouzou_aligned { ... };
> 
> 計算処理は kouzou_aligned で行う(padding あり、つまりメンバアクセスが高速)
> fwrite の際に kouzou_aligned から kouzou_packed にメンバごとコピーを行う
> fwrite は kouzou_packed で行う
> fread は逆
> 
> pros kouzou 変換処理が入る程度でソースコードはさほど難しくない
> cons せっかく変換処理を入れてるのにデータファイル互換性は無い
> 
> 案3 endianess/packing を考慮しながら「メンバごとの fwrite/fread を行う」
> pros データファイル互換なソースコードとなる
> cons ソースコードとしては複雑になる
> 
 
メリット・デメリット付きで分かりやすくご説明頂きありがとうございます。
まだまだ技術が足りないので、始めは案1で行っていきたいと思います。
技術が付き、案2・3の動きを把握できるようになりましたら、こちらも考えていきたいと思います。

長々とお付き合いいただきありがとうございました。
774RRさんのおかげでまた一歩成長することができました。

また何かありましたらご指導ご鞭撻の程よろしくお願い致します。
 

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -