0

バイナリファイルを読み取るためにこの構造体を定義しました

struct cabecera{            
  unsigned long time;
    short lrec;
    short eddimdat;
    short edmaxdat;
    short edncn;
    short estindefmax;
    long maxiedisc;
  long edbuit;
    long edusat;
    short estindefusat;
    long libdoff;
    long vidoff;
    long dgoff;
  long estindefoff;
    long estinoff;
    long sedoff;
  long esdoff;
    int libvers;
    long offie;
    long tiueoff;
};

データを読み取るために fstream から拡張するクラスがあります

open(fNombre.c_str(),ios::in|ios::binary);
if(fail()||bad()) return;

int pos = 160 ;
cabecera cb={};
seekg(pos,ios::beg);
read((char*)&cb, sizeof(cb));

しかし、変数 maxiedisc は間違った値 (1052835858) を取得し、残りの変数もここから取得します。この変数を構造体なしで読み取ると、取得した値は正しい (1200000) です。

int tmLong = sizeof(long);
int tmULong = sizeof(unsigned long);
int tmShort = sizeof(short);   
int pos = 160 + tmULong  + (tmShort*5);
seekg(pos,ios::beg);
long maxiedisc;                        
read((char*)&maxiedisc, tmLong);

構造上の問題は何ですか?異なる結果が得られるのはなぜですか?

4

7 に答える 7

1

ほとんどの場合、構造体にパディングがあります。コンパイラは、estindefmax メンバーと maxiedisc メンバーの間に 2 バイト余分に配置しました。これが、まったく同じ構造体でファイルを作成しない限り、構造体に直接読み取ることが悪い考えである理由です。

構造体なしで、2番目の方法でそれを行います。それが必要な場合は、後で構造体に記入してください。

于 2012-07-18T09:15:58.993 に答える
1

ファイルをメモリからディスクに直接読み書きすることは、移植性がありません。

あなたが抱えているかもしれない問題のいくつかは

  • メモリのパディング。(コンパイラ依存) #pragma pack(vs) を使用してこれを回避できますが、これらの構造は CPU によって非効率的な方法で使用されます。
  • エンディアン ess。整数型は、リトル エンディアンまたはビッグ エンディアン形式で格納できます (プラットフォームによって異なります)。boost::endian関数ファミリーを使用して変換可能
  • 複雑なデータ構造を保存(STL リスト、ベクターなど)
  • 構造体のバージョン管理。古いバージョンの構造体を新しいプログラムにロードします。

適切なアプローチは、このすべての問題をすでにカプセル化したシリアライゼーション ライブラリを使用することです (Boost::serializationまたは google のようなものProtoBuff)。または、ライブラリのオーバーヘッドが大きすぎて自分で小さなシリアライザーを作成できない場合。思ったより簡単です。ストリームとの間でメンバーの書き込み/読み取りを行う 2 つのメンバー関数 (保存/読み込み) を記述するだけです。エンディアンとバージョン管理は自分で処理する必要があります。

于 2012-07-18T10:04:07.007 に答える
0

@john が述べたように、問題は構造体のパディングにあるようです。

パディングを取り除くには 2 つの解決策があります。1 つ目は、各構造体コンポーネントを 1 つずつ書き込むことです (ただし、そのサイズの構造体では最善の方法ではありません)。2 つ目は、他のユーザーが提案するようにパディングを無効にすることです。

#ifndef LINUX
#pragma pack(1)
#endif
struct cabecera
{
    // your stuff...
}__attribute__((packed));
#else
};
#endif

PS: コードに複数の言語を混在させないでください。ばかげているように見えます ;) "

于 2012-07-18T09:22:33.687 に答える
0

Google protobufのようなものを使用して、構造体のシリアル化/非シリアル化を行うことができます。このアプローチははるかに安全で、コンパイラとシステムの境界を越えて機能します。別の方法は、各フィールドを個別にシリアル化することです。パッキングが最も速いオプションである場合もありますが、システム間のコンパイラの互換性とバイナリの互換性に問題があります。

于 2012-07-18T09:44:36.690 に答える
0

コンパイラのパディングを無効にする必要があります。構造にパディングバイトが追加され、予想よりも大きくなります。
使用するコンパイラについて言及していないため、msvcでの方法は次のとおりです私の記憶が正しければ、gcc の構文も同じです。しかし、Ineverはそれを試しました。

于 2012-07-18T09:18:25.217 に答える
0

パディングの問題のように見えます

gcc try で構造体に直接読み込む

struct my_struct {
    /* ... */
} __attribute__((packed));

これにより、パディングが使用されないことが保証されます

于 2012-07-18T09:18:45.193 に答える
0
#pragma pack(push,1)
// struct definition
#pragma push
于 2012-07-18T09:50:49.360 に答える