• ベストアンサー

可変長構造体をファイルから読み込み処理

可変長の構造体、 typedef struct 構造体(仮) { char c1,c2; float f1,f2; double d; int size; //↓strのサイズ char str[1]; //文字配列 }構造体(仮); の形式で書かれたバイナリデータファイルがあります。 そのファイルを読み込んでcsv形式で出力する処理を、 ファイルからの読み込む回数を減らしてやりたいと思っています。 その方法を教えていただけませんか? よろしくおねがいします。

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

  • ベストアンサー
  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.5

>mallocで取れるだけ領域を取ってそこからその 処理系や環境によりますがWindowsやLinuxの場合100Mぐらいは余裕でとれてしまいます。 >構造体の途中からfreadすることって可能でしょうか? 可能です。 memcpy( (char*)s+読み込んだバイト , pBuf , structSize - (読み込んだバイト) ); みたいな感じになります。 sizeを読み込んでから切れる場合と、sizeの前で切れる場合があるので sizeの前で切れた場合は次のバッファを読み込んでから構造体のサイズを確定する処理 をする必要があるでしょう。 ただ構造体のalignmentには注意してください。 例えば typedef struct _S{ char a; short b; }S; の場合3バイトとは限りません。コンパイラの設定によっては paddingの部分で切れる可能性も考慮する必要があります。

Missing0001
質問者

お礼

お返事ありがとうございます。 alignmentというのもあるのですね・・・ そういうことも気にしなくてはならないとは・・・

その他の回答 (7)

  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.8

>mallocで取れるバッファのサイズを調べる関数とかあるのでしょうか? ありません。mallocでとれるかどうかは実際にmallocしなければわかりません。 (同じOS、同じ環境であっても、バックグラウンド等で動くプロセスの影響もありmallocでとれる最大サイズは常に変わります。) malloc→freeを繰り返すのではなく最初に必要な分はとっておくと良いでしょう。 十分なサイズを確保しておき、どうしても足りない場合はreallocをするというような。

Missing0001
質問者

お礼

ありがとうございます。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.7

実装例: #3のソースを改良したバージョン(解説は省略します、実際のテストは行っていません) /* 1つの可変長・構造体データの最大バイト数(最低でも構造体サイズ+文字配列サイズ以上) */ #define MAX_SIZE (1024 * 1024) ←可能な限り大きいサイズで読み込む回数を減らせます /* 可変長構造体のファイルからcsv形式のファイルへ出力 */ void FuncRead( FILE *fp, FILE *fo ) {  size_t size; /* 読み込んだバイト数 */  char *buff; /* 読み込むバッファ領域(先頭位置) */  char *last; /* 読み込むバッファ領域(最終位置) */  char *seek; /* 参照したバッファ位置(現在位置) */  構造体(仮) *data; /* 参照する構造体へのポインタ */    if ( (buff = malloc(MAX_SIZE)) != NULL ){   while ( (size = fread(buff,1,MAX_SIZE,fp)) != 0 ){    last = (buff + size);    seek = (buff);    data = (構造体(仮) *)seek;        while ( (data->str + data->size - 1) < last ){     FuncWrite( data, fo ); ←構造体(仮)のデータを出力させる関数     seek += (sizeof(構造体(仮)) + data->size - 1);     data = (構造体(仮) *)seek;    }    if ( feof(fp) ){     break;    }    fseek( fp, -(size_t)(last - seek), SEEK_CUR ); ←ファイルポインタを戻る   }   free( buff );  } } /* ●引数 (1)入力ファイルのポインタを fp で指定 (2)出力ファイルのポインタを fo で指定 ●その他 void FuncWrite( 構造体(仮) *data, FILE fo ); ●注意 ・1つの可変長・構造体データは、構造体サイズ+data->size-1ですよ。 ・文字配列が文字列ならば、NULL文字分を考えて上記ソースの-1は削除しましょう。 */

Missing0001
質問者

お礼

改良版ありがとうございます。 とりあえず内容を見てみました。 このソースの処理は、 > while ( (data->str + data->size - 1) < last ){ > fseek( fp, -(size_t)(last - seek), SEEK_CUR ); ←ファイルポインタを戻る のところを見る限り構造体全体が読めなかったら ファイルポインタを読めなかった構造体の最初に戻って読み直すという 処理をしているみたいですね。 構造体の途中から読むということもしないので無難かもしれませんね。

  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.6

#5です。追記です。 例えば1024バイトのバッファで何回も読み込んでいくような方式を考えた場合に 構造体のsizeが5000バイトを超えるようなパターンもありますね。

Missing0001
質問者

補足

あ、多分これは大丈夫です。 10Mぐらいのバッファで構造体もバッファより大きいものは存在しない予定なので・・・ それとついでの質問なのですが、 mallocで取れるバッファのサイズを調べる関数とかあるのでしょうか?

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

ISO C から外れていいなら memmap とか CreateFileMapping とかを使うかなぁ? どちらもファイルをメモリ空間にマッピングするので, あたかも「十分な大きさのバッファに fread しか」かのように扱うことができるんじゃないかな.

Missing0001
質問者

お礼

そういえばISO Cしか考えてませんでしたね 時間があれば調べてみようと思います。

  • sha-girl
  • ベストアンサー率52% (430/816)
回答No.3

最初にftellでファイルサイズを取得しmallocで領域を確保し freadで全部メモリーに読み込めばいいのでは? そうすれな読み込み回数は1回で済みますから。 ファイルサイズが大きくmallocで確保できないような連続領域が必要なら 無理ですが。 簡単に書くとこんなかんじでしょうか。(なんのエラー処理もしていません) #include <memory.h> #include <stdio.h> #include <stdlib.h> typedef struct _STRUCT {  char c1,c2;  float f1,f2;  double d;  int size;  char str[1]; }STRUCT; int main() {  size_t structSize;  STRUCT* s;  char* pBuf;  size_t FileSize;  size_t ReadSize = 0;  FILE* fp = fopen("hoge.dat","rb");  fseek(fp,0,SEEK_END);  FileSize = ftell(fp);  printf("FileSize=%u\n",FileSize);  fseek(fp,0,SEEK_SET);  pBuf = (char*)malloc(FileSize);  fread(pBuf,FileSize,1,fp);  fclose(fp);  while( ReadSize < FileSize ){   structSize = sizeof(STRUCT) - sizeof(char) + ((STRUCT*)pBuf)->size;   s = (STRUCT*)malloc(structSize);   memcpy( s , pBuf , structSize );   printf("%s\n",s->str);   free( s );   ReadSize += structSize;   pBuf+=structSize;  }  free(pBuf); }

Missing0001
質問者

補足

ありがとうです。 自分のしたかったことに近いです。 ですがファイルサイズがでかくなることも想定しているので mallocで取れるだけ領域を取ってそこからその処理をして、 領域の最後に達したらファイルから読み直すという処理をつけたいと思ってます。 ですがそうした場合、構造体の途中までしか読めていない状況が発生すると思っています。 その場合欠けた部分より後はfreadで読み込む処理を入れなくてはいけない気がします。 ですが構造体の途中からfreadすることって可能でしょうか?

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

typedef struct 構造体(仮) { char c1, c2; float f1, f2; double d; int size; chear str[1]; } 構造体(仮); の形だと (構造体タグって必要?), 実際に使うにはこの構造体に対して malloc しないといけないんだけどその辺は OK? まあいいとして, 基本的には 0.仮にデータを保存しておく構造体を用意する 1.size まで fread 2.その size に従って構造体を malloc する 3.size のところまで memcpy 4,size の分だけ fread の順. 今の ISO C なら str を char str[]; で宣言できて, この場合には { 構造体(仮) dummy, *data; fread(&dummy, sizeof dummy, 1, fp); data = malloc(sizeof *data + dummy.size); memcpy(&dummy, data, sizeof dummy); fread(data->str, 1, data->size, fp); } くらいになるかと.

Missing0001
質問者

お礼

1の方の方法をソースで書いてくれたのですね。 ありがとうです。 ・・・ですが1の方のところで書いたとおりその方法は考えてはいました。 もう少し詳しく書いておくべきでしたね、すみません。

  • chirubou
  • ベストアンサー率37% (189/502)
回答No.1

私だったら、 void hoge( int fd ) { 構造体(仮) kari; int sz, cc; char *str; sz = (int) ( (void*) &kari - (void*) &kari.str ); cc = read( fd, &kari, sz ); /* cc をチェック */ str = (char*) malloc( kari.size ); /* str をチェック */ cc = read( fd, str, kari.size ); /* cc をチェック */ } てな感じでしょうか。要は構造体(仮)のsizeまで最初に読みこんでから、size を見て、文字列を読むだけです。 ファイルを読み込む回数ですが、Linux とか最近のOSではメモリにキャッシュされるので、回数は昔ほど気にしなくても大丈夫です。

Missing0001
質問者

お礼

お返事ありがとうございます。 ですがこの方法は私も考えてました。 ごめんなさいです。

関連するQ&A